diff --git a/amd/build/constants.min.js b/amd/build/constants.min.js
index 7fc3ec6..342ce4b 100644
--- a/amd/build/constants.min.js
+++ b/amd/build/constants.min.js
@@ -1,3 +1,3 @@
-define("tiny_ai/constants",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.constants=void 0;_exports.constants={modalModes:{selection:"selection",general:"general"},toolPurposeMapping:{audiogen:"tts",summarize:"singleprompt",translate:"translate",describe:"singleprompt",imggen:"imggen",tts:"tts",freeprompt:"singleprompt"}}}));
+define("tiny_ai/constants",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.constants=void 0;_exports.constants={modalModes:{selection:"selection",general:"general"},toolPurposeMapping:{audiogen:"tts",summarize:"singleprompt",translate:"translate",describe:"singleprompt",imggen:"imggen",tts:"tts",freeprompt:"singleprompt",describeimg:"itt",imagetotext:"itt"}}}));
//# sourceMappingURL=constants.min.js.map
\ No newline at end of file
diff --git a/amd/build/constants.min.js.map b/amd/build/constants.min.js.map
index 38823d4..2eab48b 100644
--- a/amd/build/constants.min.js.map
+++ b/amd/build/constants.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"constants.min.js","sources":["../src/constants.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 .\n\n/**\n * Tiny AI constants definition.\n *\n * @module tiny_ai/constants\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\nexport const constants = {\n modalModes: {\n selection: 'selection',\n general: 'general'\n },\n toolPurposeMapping: {\n audiogen: 'tts',\n summarize: 'singleprompt',\n translate: 'translate',\n describe: 'singleprompt',\n imggen: 'imggen',\n tts: 'tts',\n freeprompt: 'singleprompt',\n }\n};\n"],"names":["modalModes","selection","general","toolPurposeMapping","audiogen","summarize","translate","describe","imggen","tts","freeprompt"],"mappings":"gKAyByB,CACrBA,WAAY,CACRC,UAAW,YACXC,QAAS,WAEbC,mBAAoB,CAChBC,SAAU,MACVC,UAAW,eACXC,UAAW,YACXC,SAAU,eACVC,OAAQ,SACRC,IAAK,MACLC,WAAY"}
\ No newline at end of file
+{"version":3,"file":"constants.min.js","sources":["../src/constants.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 .\n\n/**\n * Tiny AI constants definition.\n *\n * @module tiny_ai/constants\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\nexport const constants = {\n modalModes: {\n selection: 'selection',\n general: 'general'\n },\n toolPurposeMapping: {\n audiogen: 'tts',\n summarize: 'singleprompt',\n translate: 'translate',\n describe: 'singleprompt',\n imggen: 'imggen',\n tts: 'tts',\n freeprompt: 'singleprompt',\n describeimg: 'itt',\n imagetotext: 'itt'\n }\n};\n"],"names":["modalModes","selection","general","toolPurposeMapping","audiogen","summarize","translate","describe","imggen","tts","freeprompt","describeimg","imagetotext"],"mappings":"gKAyByB,CACrBA,WAAY,CACRC,UAAW,YACXC,QAAS,WAEbC,mBAAoB,CAChBC,SAAU,MACVC,UAAW,eACXC,UAAW,YACXC,SAAU,eACVC,OAAQ,SACRC,IAAK,MACLC,WAAY,eACZC,YAAa,MACbC,YAAa"}
\ No newline at end of file
diff --git a/amd/build/controllers/base.min.js b/amd/build/controllers/base.min.js
index 1b7b69e..b407fc4 100644
--- a/amd/build/controllers/base.min.js
+++ b/amd/build/controllers/base.min.js
@@ -8,6 +8,6 @@ define("tiny_ai/controllers/base",["exports","core/notification","tiny_ai/dataha
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler);return _exports.default=class{constructor(baseSelector){_defineProperty(this,"uniqid",null),_defineProperty(this,"baseElement",null),_defineProperty(this,"renderer",null),_defineProperty(this,"editorUtils",null),_defineProperty(this,"footer",null),this.baseElement=document.querySelector(baseSelector),this.uniqid=(0,_utils.getCurrentModalUniqId)(this.baseElement),this.renderer=(0,_utils.getRenderer)(this.uniqid),this.editorUtils=(0,_utils.getEditorUtils)(this.uniqid),this.datamanager=(0,_utils.getDatamanager)(this.uniqid),null!==this.baseElement&&(this.footer=this.baseElement.parentElement.parentElement.querySelector('[data-region="footer"]'))}async generateAiAnswer(){if(null===this.datamanager.getCurrentPrompt()||0===this.datamanager.getCurrentPrompt().length)return await(0,_utils.errorAlert)(BasedataHandler.getTinyAiString("error_nopromptgiven")),null;await this.renderer.renderLoading();let result=null;try{result=await(0,_utils.getAiAnswer)(this.datamanager.getCurrentPrompt(),_constants.constants.toolPurposeMapping[this.datamanager.getCurrentTool()],this.datamanager.getCurrentOptions())}catch(exception){await(0,_notification.exception)(exception)}return null===result?(await this.callRendererFunction(),null):(this.datamanager.setCurrentAiResult(result),!0)}async callRendererFunction(){if("freeprompt"===this.datamanager.getCurrentTool())return void await this.renderer.renderStart();const toolNameWithUppercaseLetter=this.datamanager.getCurrentTool().charAt(0).toUpperCase()+this.datamanager.getCurrentTool().slice(1);this.renderer["render"+toolNameWithUppercaseLetter]()}},_exports.default}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler);return _exports.default=class{constructor(baseSelector){_defineProperty(this,"uniqid",null),_defineProperty(this,"baseElement",null),_defineProperty(this,"renderer",null),_defineProperty(this,"editorUtils",null),_defineProperty(this,"footer",null),this.baseElement=document.querySelector(baseSelector),this.uniqid=(0,_utils.getCurrentModalUniqId)(this.baseElement),this.renderer=(0,_utils.getRenderer)(this.uniqid),this.editorUtils=(0,_utils.getEditorUtils)(this.uniqid),this.datamanager=(0,_utils.getDatamanager)(this.uniqid),null!==this.baseElement&&(this.footer=this.baseElement.parentElement.parentElement.querySelector('[data-region="footer"]'))}async generateAiAnswer(){if(null===this.datamanager.getCurrentPrompt()||0===this.datamanager.getCurrentPrompt().length)return await(0,_utils.errorAlert)(BasedataHandler.getTinyAiString("error_nopromptgiven")),null;if(["describeimg","imagetotext"].includes(this.datamanager.getCurrentTool())&&null===this.datamanager.getCurrentFile())return await(0,_utils.errorAlert)(BasedataHandler.getTinyAiString("error_nofile")),null;await this.renderer.renderLoading();let result=null;try{result=await(0,_utils.getAiAnswer)(this.datamanager.getCurrentPrompt(),_constants.constants.toolPurposeMapping[this.datamanager.getCurrentTool()],this.datamanager.getCurrentOptions())}catch(exception){await(0,_notification.exception)(exception)}return null===result?(await this.callRendererFunction(),null):(this.datamanager.setCurrentAiResult(result),!0)}async callRendererFunction(){if("freeprompt"===this.datamanager.getCurrentTool())return void await this.renderer.renderStart();const toolNameWithUppercaseLetter=this.datamanager.getCurrentTool().charAt(0).toUpperCase()+this.datamanager.getCurrentTool().slice(1);this.renderer["render"+toolNameWithUppercaseLetter]()}},_exports.default}));
//# sourceMappingURL=base.min.js.map
\ No newline at end of file
diff --git a/amd/build/controllers/base.min.js.map b/amd/build/controllers/base.min.js.map
index 5ee45b6..d887b2f 100644
--- a/amd/build/controllers/base.min.js.map
+++ b/amd/build/controllers/base.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"base.min.js","sources":["../../src/controllers/base.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 .\n\nimport {exception as displayException} from 'core/notification';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport {getRenderer, getDatamanager, getAiAnswer, errorAlert, getCurrentModalUniqId, getEditorUtils} from 'tiny_ai/utils';\nimport {constants} from 'tiny_ai/constants';\n\n/**\n * Base controller class providing some basic functionalities.\n *\n * All tiny_ai controllers should inherit from this class.\n *\n * @module tiny_ai/controllers/base\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n uniqid = null;\n baseElement = null;\n renderer = null;\n editorUtils = null;\n footer = null;\n\n constructor(baseSelector) {\n this.baseElement = document.querySelector(baseSelector);\n this.uniqid = getCurrentModalUniqId(this.baseElement);\n this.renderer = getRenderer(this.uniqid);\n this.editorUtils = getEditorUtils(this.uniqid);\n this.datamanager = getDatamanager(this.uniqid);\n\n if (this.baseElement === null) {\n // Sometimes (for example we display an error message before we even finish rendering the modal) we do not have\n // a base element. In this case there is nothing to do, so we avoid console errors by early exiting.\n return;\n }\n this.footer = this.baseElement.parentElement.parentElement.querySelector('[data-region=\"footer\"]');\n }\n\n async generateAiAnswer() {\n if (this.datamanager.getCurrentPrompt() === null || this.datamanager.getCurrentPrompt().length === 0) {\n await errorAlert(BasedataHandler.getTinyAiString('error_nopromptgiven'));\n return null;\n }\n await this.renderer.renderLoading();\n let result = null;\n try {\n result = await getAiAnswer(this.datamanager.getCurrentPrompt(),\n constants.toolPurposeMapping[this.datamanager.getCurrentTool()], this.datamanager.getCurrentOptions());\n } catch (exception) {\n await displayException(exception);\n }\n\n if (result === null) {\n await this.callRendererFunction();\n return null;\n }\n this.datamanager.setCurrentAiResult(result);\n return true;\n }\n\n async callRendererFunction() {\n if (this.datamanager.getCurrentTool() === 'freeprompt') {\n await this.renderer.renderStart();\n return;\n }\n const toolNameWithUppercaseLetter =\n this.datamanager.getCurrentTool().charAt(0).toUpperCase() + this.datamanager.getCurrentTool().slice(1);\n this.renderer['render' + toolNameWithUppercaseLetter]();\n }\n}\n"],"names":["constructor","baseSelector","baseElement","document","querySelector","uniqid","this","renderer","editorUtils","datamanager","footer","parentElement","getCurrentPrompt","length","BasedataHandler","getTinyAiString","renderLoading","result","constants","toolPurposeMapping","getCurrentTool","getCurrentOptions","exception","callRendererFunction","setCurrentAiResult","renderStart","toolNameWithUppercaseLetter","charAt","toUpperCase","slice"],"mappings":";;;;;;;;;;iyBAsCIA,YAAYC,4CANH,yCACK,sCACH,yCACG,oCACL,WAGAC,YAAcC,SAASC,cAAcH,mBACrCI,QAAS,gCAAsBC,KAAKJ,kBACpCK,UAAW,sBAAYD,KAAKD,aAC5BG,aAAc,yBAAeF,KAAKD,aAClCI,aAAc,yBAAeH,KAAKD,QAEd,OAArBC,KAAKJ,mBAKJQ,OAASJ,KAAKJ,YAAYS,cAAcA,cAAcP,cAAc,uDAI7B,OAAxCE,KAAKG,YAAYG,oBAA8E,IAA/CN,KAAKG,YAAYG,mBAAmBC,oBAC9E,qBAAWC,gBAAgBC,gBAAgB,wBAC1C,WAELT,KAAKC,SAASS,oBAChBC,OAAS,SAETA,aAAe,sBAAYX,KAAKG,YAAYG,mBACxCM,qBAAUC,mBAAmBb,KAAKG,YAAYW,kBAAmBd,KAAKG,YAAYY,qBACxF,MAAOC,iBACC,2BAAiBA,kBAGZ,OAAXL,cACMX,KAAKiB,uBACJ,YAENd,YAAYe,mBAAmBP,SAC7B,mCAImC,eAAtCX,KAAKG,YAAYW,mCACXd,KAAKC,SAASkB,oBAGlBC,4BACFpB,KAAKG,YAAYW,iBAAiBO,OAAO,GAAGC,cAAgBtB,KAAKG,YAAYW,iBAAiBS,MAAM,QACnGtB,SAAS,SAAWmB"}
\ No newline at end of file
+{"version":3,"file":"base.min.js","sources":["../../src/controllers/base.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 .\n\nimport {exception as displayException} from 'core/notification';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport {getRenderer, getDatamanager, getAiAnswer, errorAlert, getCurrentModalUniqId, getEditorUtils} from 'tiny_ai/utils';\nimport {constants} from 'tiny_ai/constants';\n\n/**\n * Base controller class providing some basic functionalities.\n *\n * All tiny_ai controllers should inherit from this class.\n *\n * @module tiny_ai/controllers/base\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n uniqid = null;\n baseElement = null;\n renderer = null;\n editorUtils = null;\n footer = null;\n\n constructor(baseSelector) {\n this.baseElement = document.querySelector(baseSelector);\n this.uniqid = getCurrentModalUniqId(this.baseElement);\n this.renderer = getRenderer(this.uniqid);\n this.editorUtils = getEditorUtils(this.uniqid);\n this.datamanager = getDatamanager(this.uniqid);\n\n if (this.baseElement === null) {\n // Sometimes (for example we display an error message before we even finish rendering the modal) we do not have\n // a base element. In this case there is nothing to do, so we avoid console errors by early exiting.\n return;\n }\n this.footer = this.baseElement.parentElement.parentElement.querySelector('[data-region=\"footer\"]');\n }\n\n async generateAiAnswer() {\n if (this.datamanager.getCurrentPrompt() === null || this.datamanager.getCurrentPrompt().length === 0) {\n await errorAlert(BasedataHandler.getTinyAiString('error_nopromptgiven'));\n return null;\n }\n if (['describeimg', 'imagetotext'].includes(this.datamanager.getCurrentTool())\n && this.datamanager.getCurrentFile() === null) {\n await errorAlert(BasedataHandler.getTinyAiString('error_nofile'));\n return null;\n }\n await this.renderer.renderLoading();\n let result = null;\n try {\n result = await getAiAnswer(this.datamanager.getCurrentPrompt(),\n constants.toolPurposeMapping[this.datamanager.getCurrentTool()], this.datamanager.getCurrentOptions());\n } catch (exception) {\n await displayException(exception);\n }\n\n if (result === null) {\n await this.callRendererFunction();\n return null;\n }\n this.datamanager.setCurrentAiResult(result);\n return true;\n }\n\n async callRendererFunction() {\n if (this.datamanager.getCurrentTool() === 'freeprompt') {\n await this.renderer.renderStart();\n return;\n }\n const toolNameWithUppercaseLetter =\n this.datamanager.getCurrentTool().charAt(0).toUpperCase() + this.datamanager.getCurrentTool().slice(1);\n this.renderer['render' + toolNameWithUppercaseLetter]();\n }\n}\n"],"names":["constructor","baseSelector","baseElement","document","querySelector","uniqid","this","renderer","editorUtils","datamanager","footer","parentElement","getCurrentPrompt","length","BasedataHandler","getTinyAiString","includes","getCurrentTool","getCurrentFile","renderLoading","result","constants","toolPurposeMapping","getCurrentOptions","exception","callRendererFunction","setCurrentAiResult","renderStart","toolNameWithUppercaseLetter","charAt","toUpperCase","slice"],"mappings":";;;;;;;;;;iyBAsCIA,YAAYC,4CANH,yCACK,sCACH,yCACG,oCACL,WAGAC,YAAcC,SAASC,cAAcH,mBACrCI,QAAS,gCAAsBC,KAAKJ,kBACpCK,UAAW,sBAAYD,KAAKD,aAC5BG,aAAc,yBAAeF,KAAKD,aAClCI,aAAc,yBAAeH,KAAKD,QAEd,OAArBC,KAAKJ,mBAKJQ,OAASJ,KAAKJ,YAAYS,cAAcA,cAAcP,cAAc,uDAI7B,OAAxCE,KAAKG,YAAYG,oBAA8E,IAA/CN,KAAKG,YAAYG,mBAAmBC,oBAC9E,qBAAWC,gBAAgBC,gBAAgB,wBAC1C,QAEP,CAAC,cAAe,eAAeC,SAASV,KAAKG,YAAYQ,mBACZ,OAAtCX,KAAKG,YAAYS,8BAClB,qBAAWJ,gBAAgBC,gBAAgB,iBAC1C,WAELT,KAAKC,SAASY,oBAChBC,OAAS,SAETA,aAAe,sBAAYd,KAAKG,YAAYG,mBACxCS,qBAAUC,mBAAmBhB,KAAKG,YAAYQ,kBAAmBX,KAAKG,YAAYc,qBACxF,MAAOC,iBACC,2BAAiBA,kBAGZ,OAAXJ,cACMd,KAAKmB,uBACJ,YAENhB,YAAYiB,mBAAmBN,SAC7B,mCAImC,eAAtCd,KAAKG,YAAYQ,mCACXX,KAAKC,SAASoB,oBAGlBC,4BACFtB,KAAKG,YAAYQ,iBAAiBY,OAAO,GAAGC,cAAgBxB,KAAKG,YAAYQ,iBAAiBc,MAAM,QACnGxB,SAAS,SAAWqB"}
\ No newline at end of file
diff --git a/amd/build/controllers/file.min.js b/amd/build/controllers/file.min.js
new file mode 100644
index 0000000..6bd9c98
--- /dev/null
+++ b/amd/build/controllers/file.min.js
@@ -0,0 +1,3 @@
+define("tiny_ai/controllers/file",["exports","tiny_ai/utils","core/templates","tiny_ai/selectors","tiny_ai/datahandler/basedata","core/str"],(function(_exports,_utils,_templates,_selectors,BasedataHandler,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler);return _exports.default=class{constructor(baseSelector){_defineProperty(this,"dropzone",null),_defineProperty(this,"dropzoneContentToResetTo",""),this.baseElement=document.querySelector(baseSelector)}async init(){this.dropzone=this.baseElement.querySelector('[data-type="dropzone"]');const dropzone=this.dropzone;dropzone.contentEditable=!0,this.setDropzoneContent(dropzone.innerHTML);const _this=this;dropzone.addEventListener("input",(()=>{dropzone.innerHTML!==_this.dropzoneContentToResetTo&&(dropzone.innerHTML=_this.dropzoneContentToResetTo)})),dropzone.addEventListener("drop",(async event=>{if(event.preventDefault(),event.dataTransfer.items){const item=[...event.dataTransfer.items].shift();"file"===item.kind&&await this.handleFile(item.getAsFile())}else await this.handleFile([...event.dataTransfer.files].shift())})),document.querySelector(_selectors.default.modalDialog).addEventListener("paste",(async event=>{event.preventDefault();const clipboardData=event.clipboardData||window.clipboardData;if(0===clipboardData.files.length)return void await(0,_utils.errorAlert)(BasedataHandler.getTinyAiString("error_nofileinclipboard_text"),BasedataHandler.getTinyAiString("error_nofileinclipboard_title"));const file=clipboardData.files[0];this.handleFile(file)})),dropzone.addEventListener("dragover",(event=>{event.preventDefault(),dropzone.classList.remove("tiny_ai_dropzone_filled"),dropzone.classList.add("tiny_ai_dragover")})),dropzone.addEventListener("dragleave",(event=>{event.preventDefault(),dropzone.classList.remove("tiny_ai_dragover")}));const datamanager=(0,_utils.getDatamanager)((0,_utils.getCurrentModalUniqId)(this.baseElement));null!==datamanager.getSelectionImg()&&await this.handleFile(datamanager.getSelectionImg())}async handleFile(file){const reader=new FileReader,_this=this;reader.addEventListener("load",(async()=>{const currentModalUniqid=(0,_utils.getCurrentModalUniqId)(this.baseElement),datamanager=(0,_utils.getDatamanager)(currentModalUniqid),fileUploadedEvent=new CustomEvent("fileUploaded",{detail:{newFile:reader.result}});datamanager.getEventEmitterElement().dispatchEvent(fileUploadedEvent);const ittHandler=(0,_utils.getIttHandler)(currentModalUniqid),allowedMimetypes=await ittHandler.getAllowedMimetypes();if(!allowedMimetypes.includes(file.type)){const errorTitle=await(0,_str.getString)("error_unsupportedfiletype_title","tiny_ai"),errorText=await(0,_str.getString)("error_unsupportedfiletype_text","tiny_ai",allowedMimetypes.toString());return void await(0,_utils.errorAlert)(errorText,errorTitle)}const fileEntryTemplateContext={icon:"application/pdf"===file.type?"fa-file-pdf":"fa-image",filename:file.name?file.name:BasedataHandler.getTinyAiString("imagefromeditor")};file.type.startsWith("image")&&(fileEntryTemplateContext.isImage=!0,fileEntryTemplateContext.dataurl=reader.result);const{html:html,js:js}=await _templates.default.renderForPromise("tiny_ai/components/ai-file-list-entry",fileEntryTemplateContext);_this.setDropzoneContent(html),_templates.default.runTemplateJS(js),_this.dropzone.classList.remove("tiny_ai_dragover"),_this.dropzone.classList.add("tiny_ai_dropzone_filled")}),!1),reader.readAsDataURL(file)}setDropzoneContent(html){this.dropzone.innerHTML=html,this.dropzoneContentToResetTo=html}},_exports.default}));
+
+//# sourceMappingURL=file.min.js.map
\ No newline at end of file
diff --git a/amd/build/controllers/file.min.js.map b/amd/build/controllers/file.min.js.map
new file mode 100644
index 0000000..2347d86
--- /dev/null
+++ b/amd/build/controllers/file.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"file.min.js","sources":["../../src/controllers/file.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 .\n\n/**\n * Controller for handling the show/hide prompt button and the associated textarea.\n *\n * @module tiny_ai/controllers/file\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getDatamanager, getCurrentModalUniqId, getIttHandler} from 'tiny_ai/utils';\nimport Templates from 'core/templates';\nimport SELECTORS from 'tiny_ai/selectors';\nimport {errorAlert} from 'tiny_ai/utils';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport {getString} from 'core/str';\n\n\nexport default class {\n\n dropzone = null;\n dropzoneContentToResetTo = '';\n\n constructor(baseSelector) {\n this.baseElement = document.querySelector(baseSelector);\n }\n\n async init() {\n this.dropzone = this.baseElement.querySelector('[data-type=\"dropzone\"]');\n const dropzone = this.dropzone;\n // Setting contentEditable to true makes the browser show a \"paste\" option in the context menu when\n // right-clicking the drop zone.\n dropzone.contentEditable = true;\n this.setDropzoneContent(dropzone.innerHTML);\n\n const _this = this;\n // The drop zone has \"contentEditable\" enabled, so we have to take care of user input\n // and reset the content whenever a user tries to input something.\n dropzone.addEventListener('input', () => {\n if (dropzone.innerHTML !== _this.dropzoneContentToResetTo) {\n dropzone.innerHTML = _this.dropzoneContentToResetTo;\n }\n });\n dropzone.addEventListener('drop', async(event) => {\n event.preventDefault();\n\n if (event.dataTransfer.items) {\n // Use DataTransferItemList interface to access the file(s)\n const item = [...event.dataTransfer.items].shift();\n // If dropped item is no file, reject it.\n if (item.kind === 'file') {\n await this.handleFile(item.getAsFile());\n }\n } else {\n // Use DataTransfer interface to access the file(s)\n await this.handleFile([...event.dataTransfer.files].shift());\n }\n });\n document.querySelector(SELECTORS.modalDialog).addEventListener('paste', async(event) => {\n event.preventDefault();\n const clipboardData = (event.clipboardData || window.clipboardData);\n if (clipboardData.files.length === 0) {\n await errorAlert(BasedataHandler.getTinyAiString('error_nofileinclipboard_text'),\n BasedataHandler.getTinyAiString('error_nofileinclipboard_title'));\n return;\n }\n const file = clipboardData.files[0];\n this.handleFile(file);\n });\n dropzone.addEventListener('dragover', (event) => {\n event.preventDefault();\n dropzone.classList.remove('tiny_ai_dropzone_filled');\n dropzone.classList.add('tiny_ai_dragover');\n });\n dropzone.addEventListener('dragleave', (event) => {\n event.preventDefault();\n dropzone.classList.remove('tiny_ai_dragover');\n });\n\n const datamanager = getDatamanager(getCurrentModalUniqId(this.baseElement));\n if (datamanager.getSelectionImg() !== null) {\n await this.handleFile(datamanager.getSelectionImg());\n }\n }\n\n async handleFile(file) {\n const reader = new FileReader();\n const _this = this;\n reader.addEventListener(\n 'load',\n async() => {\n const currentModalUniqid = getCurrentModalUniqId(this.baseElement);\n const datamanager = getDatamanager(currentModalUniqid);\n const fileUploadedEvent = new CustomEvent('fileUploaded', {\n detail: {\n newFile: reader.result,\n }\n });\n datamanager.getEventEmitterElement().dispatchEvent(fileUploadedEvent);\n const ittHandler = getIttHandler(currentModalUniqid);\n const allowedMimetypes = await ittHandler.getAllowedMimetypes();\n\n if (!allowedMimetypes.includes(file.type)) {\n const errorTitle = await getString('error_unsupportedfiletype_title', 'tiny_ai');\n const errorText = await getString('error_unsupportedfiletype_text', 'tiny_ai', allowedMimetypes.toString());\n await errorAlert(errorText, errorTitle);\n return;\n }\n\n const fileEntryTemplateContext = {\n icon: file.type === 'application/pdf' ? 'fa-file-pdf' : 'fa-image',\n filename: file.name ? file.name : BasedataHandler.getTinyAiString('imagefromeditor'),\n };\n if (file.type.startsWith('image')) {\n fileEntryTemplateContext.isImage = true;\n fileEntryTemplateContext.dataurl = reader.result;\n }\n const {html, js} = await Templates.renderForPromise('tiny_ai/components/ai-file-list-entry',\n fileEntryTemplateContext);\n _this.setDropzoneContent(html);\n // We probably have no JS, but let's be safe here.\n Templates.runTemplateJS(js);\n // There should be no tiny_ai_dragover class, just to be safe.\n _this.dropzone.classList.remove('tiny_ai_dragover');\n _this.dropzone.classList.add('tiny_ai_dropzone_filled');\n },\n false,\n );\n reader.readAsDataURL(file);\n }\n\n setDropzoneContent(html) {\n this.dropzone.innerHTML = html;\n // Keep track of the state.\n this.dropzoneContentToResetTo = html;\n }\n}\n"],"names":["constructor","baseSelector","baseElement","document","querySelector","dropzone","this","contentEditable","setDropzoneContent","innerHTML","_this","addEventListener","dropzoneContentToResetTo","async","event","preventDefault","dataTransfer","items","item","shift","kind","handleFile","getAsFile","files","SELECTORS","modalDialog","clipboardData","window","length","BasedataHandler","getTinyAiString","file","classList","remove","add","datamanager","getSelectionImg","reader","FileReader","currentModalUniqid","fileUploadedEvent","CustomEvent","detail","newFile","result","getEventEmitterElement","dispatchEvent","ittHandler","allowedMimetypes","getAllowedMimetypes","includes","type","errorTitle","errorText","toString","fileEntryTemplateContext","icon","filename","name","startsWith","isImage","dataurl","html","js","Templates","renderForPromise","runTemplateJS","readAsDataURL"],"mappings":"olDAqCIA,YAAYC,8CAHD,sDACgB,SAGlBC,YAAcC,SAASC,cAAcH,gCAIrCI,SAAWC,KAAKJ,YAAYE,cAAc,gCACzCC,SAAWC,KAAKD,SAGtBA,SAASE,iBAAkB,OACtBC,mBAAmBH,SAASI,iBAE3BC,MAAQJ,KAGdD,SAASM,iBAAiB,SAAS,KAC3BN,SAASI,YAAcC,MAAME,2BAC7BP,SAASI,UAAYC,MAAME,6BAGnCP,SAASM,iBAAiB,QAAQE,MAAAA,WAC9BC,MAAMC,iBAEFD,MAAME,aAAaC,MAAO,OAEpBC,KAAO,IAAIJ,MAAME,aAAaC,OAAOE,QAEzB,SAAdD,KAAKE,YACCd,KAAKe,WAAWH,KAAKI,wBAIzBhB,KAAKe,WAAW,IAAIP,MAAME,aAAaO,OAAOJ,YAG5DhB,SAASC,cAAcoB,mBAAUC,aAAad,iBAAiB,SAASE,MAAAA,QACpEC,MAAMC,uBACAW,cAAiBZ,MAAMY,eAAiBC,OAAOD,iBAClB,IAA/BA,cAAcH,MAAMK,yBACd,qBAAWC,gBAAgBC,gBAAgB,gCAC7CD,gBAAgBC,gBAAgB,wCAGlCC,KAAOL,cAAcH,MAAM,QAC5BF,WAAWU,SAEpB1B,SAASM,iBAAiB,YAAaG,QACnCA,MAAMC,iBACNV,SAAS2B,UAAUC,OAAO,2BAC1B5B,SAAS2B,UAAUE,IAAI,uBAE3B7B,SAASM,iBAAiB,aAAcG,QACpCA,MAAMC,iBACNV,SAAS2B,UAAUC,OAAO,6BAGxBE,aAAc,0BAAe,gCAAsB7B,KAAKJ,cACxB,OAAlCiC,YAAYC,yBACN9B,KAAKe,WAAWc,YAAYC,oCAIzBL,YACPM,OAAS,IAAIC,WACb5B,MAAQJ,KACd+B,OAAO1B,iBACH,QACAE,gBACU0B,oBAAqB,gCAAsBjC,KAAKJ,aAChDiC,aAAc,yBAAeI,oBAC7BC,kBAAoB,IAAIC,YAAY,eAAgB,CACtDC,OAAQ,CACJC,QAASN,OAAOO,UAGxBT,YAAYU,yBAAyBC,cAAcN,yBAC7CO,YAAa,wBAAcR,oBAC3BS,uBAAyBD,WAAWE,0BAErCD,iBAAiBE,SAASnB,KAAKoB,MAAO,OACjCC,iBAAmB,kBAAU,kCAAmC,WAChEC,gBAAkB,kBAAU,iCAAkC,UAAWL,iBAAiBM,8BAC1F,qBAAWD,UAAWD,kBAI1BG,yBAA2B,CAC7BC,KAAoB,oBAAdzB,KAAKoB,KAA6B,cAAgB,WACxDM,SAAU1B,KAAK2B,KAAO3B,KAAK2B,KAAO7B,gBAAgBC,gBAAgB,oBAElEC,KAAKoB,KAAKQ,WAAW,WACrBJ,yBAAyBK,SAAU,EACnCL,yBAAyBM,QAAUxB,OAAOO,cAExCkB,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,iBAAiB,wCAChDV,0BACJ7C,MAAMF,mBAAmBsD,yBAEfI,cAAcH,IAExBrD,MAAML,SAAS2B,UAAUC,OAAO,oBAChCvB,MAAML,SAAS2B,UAAUE,IAAI,8BAEjC,GAEJG,OAAO8B,cAAcpC,MAGzBvB,mBAAmBsD,WACVzD,SAASI,UAAYqD,UAErBlD,yBAA2BkD"}
\ No newline at end of file
diff --git a/amd/build/controllers/preferences.min.js b/amd/build/controllers/preferences.min.js
index 93ffeb2..abd37b6 100644
--- a/amd/build/controllers/preferences.min.js
+++ b/amd/build/controllers/preferences.min.js
@@ -2,10 +2,10 @@ define("tiny_ai/controllers/preferences",["exports","tiny_ai/constants","tiny_ai
/**
* Controller for the main selection.
*
- * @module tiny_ai/controllers/translate
+ * @module tiny_ai/controllers/preferences
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_base=_interopRequireDefault(_base);class _default extends _base.default{async init(){const modalFooter=document.querySelector(_selectors.default.modalFooter),backButton=modalFooter.querySelector('[data-action="back"]'),generateButton=modalFooter.querySelector('[data-action="generate"]'),[summarizeHandler,translateHandler,ttsHandler,imggenHandler]=[(0,_utils.getSummarizeHandler)(this.uniqid),(0,_utils.getTranslateHandler)(this.uniqid),(0,_utils.getTtsHandler)(this.uniqid),(0,_utils.getImggenHandler)(this.uniqid)];switch(this.datamanager.getCurrentTool()){case"summarize":case"describe":{summarizeHandler.setTool(this.datamanager.getCurrentTool());const maxWordCountElement=this.baseElement.querySelector('[data-preference="maxWordCount"]'),languageTypeElement=this.baseElement.querySelector('[data-preference="languageType"]');summarizeHandler.setMaxWordCount(maxWordCountElement.querySelector('[data-dropdown="select"]').dataset.value),summarizeHandler.setLanguageType(languageTypeElement.querySelector('[data-dropdown="select"]').dataset.value);const currentPromptSummarize=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptSummarize),maxWordCountElement.addEventListener("dropdownSelectionUpdated",(async event=>{summarizeHandler.setMaxWordCount(event.detail.newValue);const currentPrompt=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPrompt)})),languageTypeElement.addEventListener("dropdownSelectionUpdated",(async event=>{summarizeHandler.setLanguageType(event.detail.newValue);const currentPrompt=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPrompt)}));break}case"translate":{const targetLanguageElement=this.baseElement.querySelector('[data-preference="targetLanguage"]');translateHandler.setTargetLanguage(targetLanguageElement.querySelector('[data-dropdown="select"]').dataset.value);const currentPromptTranslate=await translateHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptTranslate),targetLanguageElement.addEventListener("dropdownSelectionUpdated",(async event=>{translateHandler.setTargetLanguage(event.detail.newValue);const currentPromptTranslate=await translateHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptTranslate)}));break}case"tts":case"audiogen":{const ttsTargetLanguageElement=this.baseElement.querySelector('[data-preference="targetLanguage"]'),voiceElement=this.baseElement.querySelector('[data-preference="voice"]'),genderElement=this.baseElement.querySelector('[data-preference="gender"]');ttsTargetLanguageElement&&(ttsHandler.setTargetLanguage(ttsTargetLanguageElement.querySelector('[data-dropdown="select"]').dataset.value),ttsTargetLanguageElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setTargetLanguage(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),voiceElement&&(ttsHandler.setVoice(voiceElement.querySelector('[data-dropdown="select"]').dataset.value),voiceElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setVoice(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),genderElement&&(ttsHandler.setGender(genderElement.querySelector('[data-dropdown="select"]').dataset.value),genderElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setGender(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),this.datamanager.setCurrentPrompt(ttsHandler.getPrompt(this.datamanager.getCurrentTool(),this.datamanager.getSelectionText())),this.datamanager.setCurrentOptions(ttsHandler.getOptions());break}case"imggen":{const sizesElement=this.baseElement.querySelector('[data-preference="sizes"]');sizesElement&&(imggenHandler.setSize(sizesElement.querySelector('[data-dropdown="select"]').dataset.value),sizesElement.addEventListener("dropdownSelectionUpdated",(event=>{imggenHandler.setSize(event.detail.newValue),this.datamanager.setCurrentOptions(imggenHandler.getOptions())}))),this.datamanager.setCurrentPrompt(""),this.datamanager.setCurrentOptions(imggenHandler.getOptions());break}}backButton&&backButton.addEventListener("click",(async()=>{await this.renderer.renderStart(_constants.constants.modalModes.selection)})),generateButton&&generateButton.addEventListener("click",(async()=>{null!==await this.generateAiAnswer()&&await this.renderer.renderSuggestion()}))}}return _exports.default=_default,_exports.default}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_base=_interopRequireDefault(_base);class _default extends _base.default{async init(){const modalFooter=document.querySelector(_selectors.default.modalFooter),backButton=modalFooter.querySelector('[data-action="back"]'),generateButton=modalFooter.querySelector('[data-action="generate"]'),[summarizeHandler,translateHandler,ttsHandler,imggenHandler,ittHandler]=[(0,_utils.getSummarizeHandler)(this.uniqid),(0,_utils.getTranslateHandler)(this.uniqid),(0,_utils.getTtsHandler)(this.uniqid),(0,_utils.getImggenHandler)(this.uniqid),(0,_utils.getIttHandler)(this.uniqid)];switch(this.datamanager.getCurrentTool()){case"summarize":case"describe":{summarizeHandler.setTool(this.datamanager.getCurrentTool());const maxWordCountElement=this.baseElement.querySelector('[data-preference="maxWordCount"]'),languageTypeElement=this.baseElement.querySelector('[data-preference="languageType"]');summarizeHandler.setMaxWordCount(maxWordCountElement.querySelector('[data-dropdown="select"]').dataset.value),summarizeHandler.setLanguageType(languageTypeElement.querySelector('[data-dropdown="select"]').dataset.value);const currentPromptSummarize=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptSummarize),maxWordCountElement.addEventListener("dropdownSelectionUpdated",(async event=>{summarizeHandler.setMaxWordCount(event.detail.newValue);const currentPrompt=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPrompt)})),languageTypeElement.addEventListener("dropdownSelectionUpdated",(async event=>{summarizeHandler.setLanguageType(event.detail.newValue);const currentPrompt=await summarizeHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPrompt)}));break}case"translate":{const targetLanguageElement=this.baseElement.querySelector('[data-preference="targetLanguage"]');translateHandler.setTargetLanguage(targetLanguageElement.querySelector('[data-dropdown="select"]').dataset.value);const currentPromptTranslate=await translateHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptTranslate),targetLanguageElement.addEventListener("dropdownSelectionUpdated",(async event=>{translateHandler.setTargetLanguage(event.detail.newValue);const currentPromptTranslate=await translateHandler.getPrompt(this.datamanager.getSelectionText());this.datamanager.setCurrentPrompt(currentPromptTranslate)}));break}case"tts":case"audiogen":{const ttsTargetLanguageElement=this.baseElement.querySelector('[data-preference="targetLanguage"]'),voiceElement=this.baseElement.querySelector('[data-preference="voice"]'),genderElement=this.baseElement.querySelector('[data-preference="gender"]');ttsTargetLanguageElement&&(ttsHandler.setTargetLanguage(ttsTargetLanguageElement.querySelector('[data-dropdown="select"]').dataset.value),ttsTargetLanguageElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setTargetLanguage(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),voiceElement&&(ttsHandler.setVoice(voiceElement.querySelector('[data-dropdown="select"]').dataset.value),voiceElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setVoice(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),genderElement&&(ttsHandler.setGender(genderElement.querySelector('[data-dropdown="select"]').dataset.value),genderElement.addEventListener("dropdownSelectionUpdated",(event=>{ttsHandler.setGender(event.detail.newValue),this.datamanager.setCurrentOptions(ttsHandler.getOptions())}))),this.datamanager.setCurrentPrompt(ttsHandler.getPrompt(this.datamanager.getCurrentTool(),this.datamanager.getSelectionText())),this.datamanager.setCurrentOptions(ttsHandler.getOptions());break}case"imggen":{const sizesElement=this.baseElement.querySelector('[data-preference="sizes"]');sizesElement&&(imggenHandler.setSize(sizesElement.querySelector('[data-dropdown="select"]').dataset.value),sizesElement.addEventListener("dropdownSelectionUpdated",(event=>{imggenHandler.setSize(event.detail.newValue),this.datamanager.setCurrentOptions(imggenHandler.getOptions())}))),this.datamanager.setCurrentPrompt(""),this.datamanager.setCurrentOptions(imggenHandler.getOptions());break}case"describeimg":case"imagetotext":this.baseElement.querySelector('[data-preference="fileupload"]')&&this.datamanager.getEventEmitterElement().addEventListener("fileUploaded",(async event=>{this.datamanager.setCurrentFile(event.detail.newFile),this.datamanager.setCurrentOptions(ittHandler.getOptions())})),this.datamanager.setCurrentPrompt(ittHandler.getPrompt(this.datamanager.getCurrentTool())),this.datamanager.setCurrentFile(null);break}backButton&&backButton.addEventListener("click",(async()=>{await this.renderer.renderStart(_constants.constants.modalModes.selection)})),generateButton&&generateButton.addEventListener("click",(async()=>{null!==await this.generateAiAnswer()&&await this.renderer.renderSuggestion()}))}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=preferences.min.js.map
\ No newline at end of file
diff --git a/amd/build/controllers/preferences.min.js.map b/amd/build/controllers/preferences.min.js.map
index a031877..1e98767 100644
--- a/amd/build/controllers/preferences.min.js.map
+++ b/amd/build/controllers/preferences.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"preferences.min.js","sources":["../../src/controllers/preferences.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 .\n\n/**\n * Controller for the main selection.\n *\n * @module tiny_ai/controllers/translate\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {constants} from 'tiny_ai/constants';\nimport SELECTORS from 'tiny_ai/selectors';\nimport BaseController from 'tiny_ai/controllers/base';\nimport {getSummarizeHandler, getTranslateHandler, getTtsHandler, getImggenHandler} from 'tiny_ai/utils';\n\nexport default class extends BaseController {\n\n async init() {\n const modalFooter = document.querySelector(SELECTORS.modalFooter);\n const backButton = modalFooter.querySelector('[data-action=\"back\"]');\n const generateButton = modalFooter.querySelector('[data-action=\"generate\"]');\n\n const [summarizeHandler, translateHandler, ttsHandler, imggenHandler] = [\n getSummarizeHandler(this.uniqid),\n getTranslateHandler(this.uniqid),\n getTtsHandler(this.uniqid),\n getImggenHandler(this.uniqid)\n ];\n\n switch (this.datamanager.getCurrentTool()) {\n case 'summarize':\n case 'describe': {\n summarizeHandler.setTool(this.datamanager.getCurrentTool());\n const maxWordCountElement = this.baseElement.querySelector('[data-preference=\"maxWordCount\"]');\n const languageTypeElement = this.baseElement.querySelector('[data-preference=\"languageType\"]');\n summarizeHandler.setMaxWordCount(maxWordCountElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n summarizeHandler.setLanguageType(languageTypeElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n const currentPromptSummarize = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptSummarize);\n maxWordCountElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n summarizeHandler.setMaxWordCount(event.detail.newValue);\n const currentPrompt = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPrompt);\n });\n languageTypeElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n summarizeHandler.setLanguageType(event.detail.newValue);\n const currentPrompt = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPrompt);\n });\n break;\n }\n case 'translate': {\n const targetLanguageElement = this.baseElement.querySelector('[data-preference=\"targetLanguage\"]');\n translateHandler.setTargetLanguage(targetLanguageElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n const currentPromptTranslate = await translateHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptTranslate);\n targetLanguageElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n translateHandler.setTargetLanguage(event.detail.newValue);\n const currentPromptTranslate = await translateHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptTranslate);\n });\n break;\n }\n case 'tts':\n case 'audiogen': {\n const ttsTargetLanguageElement = this.baseElement.querySelector('[data-preference=\"targetLanguage\"]');\n const voiceElement = this.baseElement.querySelector('[data-preference=\"voice\"]');\n const genderElement = this.baseElement.querySelector('[data-preference=\"gender\"]');\n if (ttsTargetLanguageElement) {\n ttsHandler.setTargetLanguage(ttsTargetLanguageElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n ttsTargetLanguageElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setTargetLanguage(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n if (voiceElement) {\n ttsHandler.setVoice(voiceElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n voiceElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setVoice(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n if (genderElement) {\n ttsHandler.setGender(genderElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n genderElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setGender(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n this.datamanager.setCurrentPrompt(ttsHandler.getPrompt(this.datamanager.getCurrentTool(),\n this.datamanager.getSelectionText()));\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n break;\n }\n case 'imggen': {\n const sizesElement = this.baseElement.querySelector('[data-preference=\"sizes\"]');\n\n if (sizesElement) {\n imggenHandler.setSize(sizesElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n sizesElement.addEventListener('dropdownSelectionUpdated', event => {\n imggenHandler.setSize(event.detail.newValue);\n this.datamanager.setCurrentOptions(imggenHandler.getOptions());\n });\n }\n this.datamanager.setCurrentPrompt('');\n this.datamanager.setCurrentOptions(imggenHandler.getOptions());\n break;\n }\n }\n\n if (backButton) {\n backButton.addEventListener('click', async () => {\n await this.renderer.renderStart(constants.modalModes.selection);\n });\n }\n\n if (generateButton) {\n generateButton.addEventListener('click', async () => {\n const result = await this.generateAiAnswer();\n if (result === null) {\n return;\n }\n await this.renderer.renderSuggestion();\n });\n }\n }\n}\n"],"names":["BaseController","modalFooter","document","querySelector","SELECTORS","backButton","generateButton","summarizeHandler","translateHandler","ttsHandler","imggenHandler","this","uniqid","datamanager","getCurrentTool","setTool","maxWordCountElement","baseElement","languageTypeElement","setMaxWordCount","dataset","value","setLanguageType","currentPromptSummarize","getPrompt","getSelectionText","setCurrentPrompt","addEventListener","async","event","detail","newValue","currentPrompt","targetLanguageElement","setTargetLanguage","currentPromptTranslate","ttsTargetLanguageElement","voiceElement","genderElement","setCurrentOptions","getOptions","setVoice","setGender","sizesElement","setSize","renderer","renderStart","constants","modalModes","selection","generateAiAnswer","renderSuggestion"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"preferences.min.js","sources":["../../src/controllers/preferences.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 .\n\n/**\n * Controller for the main selection.\n *\n * @module tiny_ai/controllers/preferences\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {constants} from 'tiny_ai/constants';\nimport SELECTORS from 'tiny_ai/selectors';\nimport BaseController from 'tiny_ai/controllers/base';\nimport {getSummarizeHandler, getTranslateHandler, getTtsHandler, getImggenHandler, getIttHandler} from 'tiny_ai/utils';\n\nexport default class extends BaseController {\n\n async init() {\n const modalFooter = document.querySelector(SELECTORS.modalFooter);\n const backButton = modalFooter.querySelector('[data-action=\"back\"]');\n const generateButton = modalFooter.querySelector('[data-action=\"generate\"]');\n\n const [summarizeHandler, translateHandler, ttsHandler, imggenHandler, ittHandler] = [\n getSummarizeHandler(this.uniqid),\n getTranslateHandler(this.uniqid),\n getTtsHandler(this.uniqid),\n getImggenHandler(this.uniqid),\n getIttHandler(this.uniqid)\n ];\n\n switch (this.datamanager.getCurrentTool()) {\n case 'summarize':\n case 'describe': {\n summarizeHandler.setTool(this.datamanager.getCurrentTool());\n const maxWordCountElement = this.baseElement.querySelector('[data-preference=\"maxWordCount\"]');\n const languageTypeElement = this.baseElement.querySelector('[data-preference=\"languageType\"]');\n summarizeHandler.setMaxWordCount(maxWordCountElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n summarizeHandler.setLanguageType(languageTypeElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n const currentPromptSummarize = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptSummarize);\n maxWordCountElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n summarizeHandler.setMaxWordCount(event.detail.newValue);\n const currentPrompt = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPrompt);\n });\n languageTypeElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n summarizeHandler.setLanguageType(event.detail.newValue);\n const currentPrompt = await summarizeHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPrompt);\n });\n break;\n }\n case 'translate': {\n const targetLanguageElement = this.baseElement.querySelector('[data-preference=\"targetLanguage\"]');\n translateHandler.setTargetLanguage(targetLanguageElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n const currentPromptTranslate = await translateHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptTranslate);\n targetLanguageElement.addEventListener('dropdownSelectionUpdated', async (event) => {\n translateHandler.setTargetLanguage(event.detail.newValue);\n const currentPromptTranslate = await translateHandler.getPrompt(this.datamanager.getSelectionText());\n this.datamanager.setCurrentPrompt(currentPromptTranslate);\n });\n break;\n }\n case 'tts':\n case 'audiogen': {\n const ttsTargetLanguageElement = this.baseElement.querySelector('[data-preference=\"targetLanguage\"]');\n const voiceElement = this.baseElement.querySelector('[data-preference=\"voice\"]');\n const genderElement = this.baseElement.querySelector('[data-preference=\"gender\"]');\n if (ttsTargetLanguageElement) {\n ttsHandler.setTargetLanguage(ttsTargetLanguageElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n ttsTargetLanguageElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setTargetLanguage(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n if (voiceElement) {\n ttsHandler.setVoice(voiceElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n voiceElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setVoice(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n if (genderElement) {\n ttsHandler.setGender(genderElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n genderElement.addEventListener('dropdownSelectionUpdated', event => {\n ttsHandler.setGender(event.detail.newValue);\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n });\n }\n this.datamanager.setCurrentPrompt(ttsHandler.getPrompt(this.datamanager.getCurrentTool(),\n this.datamanager.getSelectionText()));\n this.datamanager.setCurrentOptions(ttsHandler.getOptions());\n break;\n }\n case 'imggen': {\n const sizesElement = this.baseElement.querySelector('[data-preference=\"sizes\"]');\n\n if (sizesElement) {\n imggenHandler.setSize(sizesElement.querySelector('[data-dropdown=\"select\"]').dataset.value);\n sizesElement.addEventListener('dropdownSelectionUpdated', event => {\n imggenHandler.setSize(event.detail.newValue);\n this.datamanager.setCurrentOptions(imggenHandler.getOptions());\n });\n }\n this.datamanager.setCurrentPrompt('');\n this.datamanager.setCurrentOptions(imggenHandler.getOptions());\n break;\n }\n case 'describeimg':\n case 'imagetotext': {\n const fileUploadArea = this.baseElement.querySelector('[data-preference=\"fileupload\"]');\n if (fileUploadArea) {\n this.datamanager.getEventEmitterElement().addEventListener('fileUploaded', async (event) => {\n this.datamanager.setCurrentFile(event.detail.newFile);\n this.datamanager.setCurrentOptions(ittHandler.getOptions());\n });\n }\n this.datamanager.setCurrentPrompt(ittHandler.getPrompt(this.datamanager.getCurrentTool()));\n this.datamanager.setCurrentFile(null);\n break;\n }\n }\n\n if (backButton) {\n backButton.addEventListener('click', async () => {\n await this.renderer.renderStart(constants.modalModes.selection);\n });\n }\n\n if (generateButton) {\n generateButton.addEventListener('click', async () => {\n const result = await this.generateAiAnswer();\n if (result === null) {\n return;\n }\n await this.renderer.renderSuggestion();\n });\n }\n }\n}\n"],"names":["BaseController","modalFooter","document","querySelector","SELECTORS","backButton","generateButton","summarizeHandler","translateHandler","ttsHandler","imggenHandler","ittHandler","this","uniqid","datamanager","getCurrentTool","setTool","maxWordCountElement","baseElement","languageTypeElement","setMaxWordCount","dataset","value","setLanguageType","currentPromptSummarize","getPrompt","getSelectionText","setCurrentPrompt","addEventListener","async","event","detail","newValue","currentPrompt","targetLanguageElement","setTargetLanguage","currentPromptTranslate","ttsTargetLanguageElement","voiceElement","genderElement","setCurrentOptions","getOptions","setVoice","setGender","sizesElement","setSize","getEventEmitterElement","setCurrentFile","newFile","renderer","renderStart","constants","modalModes","selection","generateAiAnswer","renderSuggestion"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/controllers/start.min.js b/amd/build/controllers/start.min.js
index 63630d1..0c15593 100644
--- a/amd/build/controllers/start.min.js
+++ b/amd/build/controllers/start.min.js
@@ -6,6 +6,6 @@ define("tiny_ai/controllers/start",["exports","tiny_ai/controllers/base","tiny_a
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_base=_interopRequireDefault(_base),_jquery=_interopRequireDefault(_jquery);class _default extends _base.default{async init(){if(!this.baseElement)return;const summarizeButton=this.baseElement.querySelector('[data-action="loadsummarize"]'),translateButton=this.baseElement.querySelector('[data-action="loadtranslate"]'),describeButton=this.baseElement.querySelector('[data-action="loaddescribe"]'),ttsButton=this.baseElement.querySelector('[data-action="loadtts"]'),audiogenButton=this.baseElement.querySelector('[data-action="loadaudiogen"]'),imggenButton=this.baseElement.querySelector('[data-action="loadimggen"]'),freePromptButton=this.baseElement.querySelector('[data-action="loadfreeprompt"]'),startHandler=(0,_utils.getStartHandler)(this.uniqid);await startHandler.isTinyAiDisabled()||window.matchMedia("(pointer: coarse)").matches&&document.querySelectorAll(".tiny_ai-card-button.disabled").forEach((button=>{button.parentElement.addEventListener("click",(async()=>{(0,_jquery.default)(button).tooltip("toggle")}))})),summarizeButton&&summarizeButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("summarize"),await this.renderer.renderSummarize()})),translateButton&&translateButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("translate"),await this.renderer.renderTranslate()})),describeButton&&describeButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("describe"),await this.renderer.renderDescribe()})),ttsButton&&ttsButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("tts"),await this.renderer.renderTts()})),audiogenButton&&audiogenButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("audiogen"),await this.renderer.renderAudiogen()})),imggenButton&&imggenButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("imggen"),await this.renderer.renderImggen()})),freePromptButton&&(freePromptButton.classList.contains("disabled")?await startHandler.isTinyAiDisabled()||freePromptButton.addEventListener("click",(async()=>{await(0,_utils.errorAlert)(startHandler.isToolDisabled("freeprompt",this.editorUtils.getMode()))})):freePromptButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("freeprompt"),this.datamanager.setCurrentPrompt(this.baseElement.querySelector('[data-type="freepromptinput"]').value);null!==await this.generateAiAnswer()&&await this.renderer.renderSuggestion()})))}}return _exports.default=_default,_exports.default}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_base=_interopRequireDefault(_base),_jquery=_interopRequireDefault(_jquery);class _default extends _base.default{async init(){if(!this.baseElement)return;const summarizeButton=this.baseElement.querySelector('[data-action="loadsummarize"]'),translateButton=this.baseElement.querySelector('[data-action="loadtranslate"]'),describeButton=this.baseElement.querySelector('[data-action="loaddescribe"]'),ttsButton=this.baseElement.querySelector('[data-action="loadtts"]'),audiogenButton=this.baseElement.querySelector('[data-action="loadaudiogen"]'),imggenButton=this.baseElement.querySelector('[data-action="loadimggen"]'),freePromptButton=this.baseElement.querySelector('[data-action="loadfreeprompt"]'),describeimgButton=this.baseElement.querySelector('[data-action="loaddescribeimg"]'),imagetotextButton=this.baseElement.querySelector('[data-action="loadimagetotext"]'),startHandler=(0,_utils.getStartHandler)(this.uniqid);await startHandler.isTinyAiDisabled()||window.matchMedia("(pointer: coarse)").matches&&document.querySelectorAll(".tiny_ai-card-button.disabled").forEach((button=>{button.parentElement.addEventListener("click",(async()=>{(0,_jquery.default)(button).tooltip("toggle")}))})),summarizeButton&&summarizeButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("summarize"),await this.renderer.renderSummarize()})),translateButton&&translateButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("translate"),await this.renderer.renderTranslate()})),describeButton&&describeButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("describe"),await this.renderer.renderDescribe()})),ttsButton&&ttsButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("tts"),await this.renderer.renderTts()})),audiogenButton&&audiogenButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("audiogen"),await this.renderer.renderAudiogen()})),imggenButton&&imggenButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("imggen"),await this.renderer.renderImggen()})),describeimgButton&&describeimgButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("describeimg"),await this.renderer.renderDescribeimg()})),imagetotextButton&&imagetotextButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("imagetotext"),await this.renderer.renderImagetotext()})),freePromptButton&&(freePromptButton.classList.contains("disabled")?await startHandler.isTinyAiDisabled()||freePromptButton.addEventListener("click",(async()=>{await(0,_utils.errorAlert)(startHandler.isToolDisabled("freeprompt",this.editorUtils.getMode()))})):freePromptButton.addEventListener("click",(async()=>{this.datamanager.setCurrentTool("freeprompt"),this.datamanager.setCurrentPrompt(this.baseElement.querySelector('[data-type="freepromptinput"]').value);null!==await this.generateAiAnswer()&&await this.renderer.renderSuggestion()})))}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=start.min.js.map
\ No newline at end of file
diff --git a/amd/build/controllers/start.min.js.map b/amd/build/controllers/start.min.js.map
index 68eb080..465c87e 100644
--- a/amd/build/controllers/start.min.js.map
+++ b/amd/build/controllers/start.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"start.min.js","sources":["../../src/controllers/start.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 .\n\n/**\n * Controller for the main selection.\n *\n * @module tiny_ai/controllers/start\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseController from 'tiny_ai/controllers/base';\nimport {getStartHandler} from 'tiny_ai/utils';\nimport {errorAlert} from 'tiny_ai/utils';\n// We unfortunately need jquery for tooltip handling.\nimport $ from 'jquery';\n\nexport default class extends BaseController {\n\n async init() {\n if (!this.baseElement) {\n // In rare cases (display error messages etc.) we do not have a correct modal, so there is nothing to do here.\n return;\n }\n const summarizeButton = this.baseElement.querySelector('[data-action=\"loadsummarize\"]');\n const translateButton = this.baseElement.querySelector('[data-action=\"loadtranslate\"]');\n const describeButton = this.baseElement.querySelector('[data-action=\"loaddescribe\"]');\n const ttsButton = this.baseElement.querySelector('[data-action=\"loadtts\"]');\n const audiogenButton = this.baseElement.querySelector('[data-action=\"loadaudiogen\"]');\n const imggenButton = this.baseElement.querySelector('[data-action=\"loadimggen\"]');\n const freePromptButton = this.baseElement.querySelector('[data-action=\"loadfreeprompt\"]');\n\n const startHandler = getStartHandler(this.uniqid);\n\n if (!(await startHandler.isTinyAiDisabled())) {\n if(window.matchMedia(\"(pointer: coarse)\").matches) {\n // If we have a touch device, we need to manually trigger the tooltips by touching the cards.\n document.querySelectorAll('.tiny_ai-card-button.disabled').forEach(button => {\n button.parentElement.addEventListener(\n 'click', async() => {\n $(button).tooltip('toggle');\n });\n });\n }\n }\n\n if (summarizeButton) {\n summarizeButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('summarize');\n await this.renderer.renderSummarize();\n });\n }\n if (translateButton) {\n translateButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('translate');\n await this.renderer.renderTranslate();\n });\n }\n if (describeButton) {\n describeButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('describe');\n await this.renderer.renderDescribe();\n });\n }\n if (ttsButton) {\n ttsButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('tts');\n await this.renderer.renderTts();\n });\n }\n if (audiogenButton) {\n audiogenButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('audiogen');\n await this.renderer.renderAudiogen();\n });\n }\n if (imggenButton) {\n imggenButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('imggen');\n await this.renderer.renderImggen();\n });\n }\n if (freePromptButton) {\n if (!freePromptButton.classList.contains('disabled')) {\n freePromptButton.addEventListener('click', async () => {\n this.datamanager.setCurrentTool('freeprompt');\n this.datamanager.setCurrentPrompt(this.baseElement.querySelector('[data-type=\"freepromptinput\"]').value);\n const result = await this.generateAiAnswer();\n if (result === null) {\n return;\n }\n await this.renderer.renderSuggestion();\n });\n } else {\n if (!(await startHandler.isTinyAiDisabled())) {\n freePromptButton.addEventListener('click', async() => {\n await errorAlert(startHandler.isToolDisabled('freeprompt', this.editorUtils.getMode()));\n });\n }\n }\n }\n }\n}\n"],"names":["BaseController","this","baseElement","summarizeButton","querySelector","translateButton","describeButton","ttsButton","audiogenButton","imggenButton","freePromptButton","startHandler","uniqid","isTinyAiDisabled","window","matchMedia","matches","document","querySelectorAll","forEach","button","parentElement","addEventListener","async","tooltip","datamanager","setCurrentTool","renderer","renderSummarize","renderTranslate","renderDescribe","renderTts","renderAudiogen","renderImggen","classList","contains","isToolDisabled","editorUtils","getMode","setCurrentPrompt","value","generateAiAnswer","renderSuggestion"],"mappings":";;;;;;;;wLA8B6BA,+BAGhBC,KAAKC,yBAIJC,gBAAkBF,KAAKC,YAAYE,cAAc,iCACjDC,gBAAkBJ,KAAKC,YAAYE,cAAc,iCACjDE,eAAiBL,KAAKC,YAAYE,cAAc,gCAChDG,UAAYN,KAAKC,YAAYE,cAAc,2BAC3CI,eAAiBP,KAAKC,YAAYE,cAAc,gCAChDK,aAAeR,KAAKC,YAAYE,cAAc,8BAC9CM,iBAAmBT,KAAKC,YAAYE,cAAc,kCAElDO,cAAe,0BAAgBV,KAAKW,cAE9BD,aAAaE,oBAClBC,OAAOC,WAAW,qBAAqBC,SAEtCC,SAASC,iBAAiB,iCAAiCC,SAAQC,SAC/DA,OAAOC,cAAcC,iBACjB,SAASC,8BACHH,QAAQI,QAAQ,gBAMlCrB,iBACAA,gBAAgBmB,iBAAiB,SAASC,eACjCE,YAAYC,eAAe,mBAC1BzB,KAAK0B,SAASC,qBAGxBvB,iBACAA,gBAAgBiB,iBAAiB,SAASC,eACjCE,YAAYC,eAAe,mBAC1BzB,KAAK0B,SAASE,qBAGxBvB,gBACAA,eAAegB,iBAAiB,SAASC,eAChCE,YAAYC,eAAe,kBAC1BzB,KAAK0B,SAASG,oBAGxBvB,WACAA,UAAUe,iBAAiB,SAASC,eAC3BE,YAAYC,eAAe,aAC1BzB,KAAK0B,SAASI,eAGxBvB,gBACAA,eAAec,iBAAiB,SAASC,eAChCE,YAAYC,eAAe,kBAC1BzB,KAAK0B,SAASK,oBAGxBvB,cACAA,aAAaa,iBAAiB,SAASC,eAC9BE,YAAYC,eAAe,gBAC1BzB,KAAK0B,SAASM,kBAGxBvB,mBACKA,iBAAiBwB,UAAUC,SAAS,kBAWzBxB,aAAaE,oBACrBH,iBAAiBY,iBAAiB,SAASC,gBACjC,qBAAWZ,aAAayB,eAAe,aAAcnC,KAAKoC,YAAYC,eAZpF5B,iBAAiBY,iBAAiB,SAASC,eAClCE,YAAYC,eAAe,mBAC3BD,YAAYc,iBAAiBtC,KAAKC,YAAYE,cAAc,iCAAiCoC,OAEnF,aADMvC,KAAKwC,0BAIpBxC,KAAK0B,SAASe"}
\ No newline at end of file
+{"version":3,"file":"start.min.js","sources":["../../src/controllers/start.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 .\n\n/**\n * Controller for the main selection.\n *\n * @module tiny_ai/controllers/start\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseController from 'tiny_ai/controllers/base';\nimport {getStartHandler} from 'tiny_ai/utils';\nimport {errorAlert} from 'tiny_ai/utils';\n// We unfortunately need jquery for tooltip handling.\nimport $ from 'jquery';\n\nexport default class extends BaseController {\n\n async init() {\n if (!this.baseElement) {\n // In rare cases (display error messages etc.) we do not have a correct modal, so there is nothing to do here.\n return;\n }\n const summarizeButton = this.baseElement.querySelector('[data-action=\"loadsummarize\"]');\n const translateButton = this.baseElement.querySelector('[data-action=\"loadtranslate\"]');\n const describeButton = this.baseElement.querySelector('[data-action=\"loaddescribe\"]');\n const ttsButton = this.baseElement.querySelector('[data-action=\"loadtts\"]');\n const audiogenButton = this.baseElement.querySelector('[data-action=\"loadaudiogen\"]');\n const imggenButton = this.baseElement.querySelector('[data-action=\"loadimggen\"]');\n const freePromptButton = this.baseElement.querySelector('[data-action=\"loadfreeprompt\"]');\n const describeimgButton = this.baseElement.querySelector('[data-action=\"loaddescribeimg\"]');\n const imagetotextButton = this.baseElement.querySelector('[data-action=\"loadimagetotext\"]');\n\n const startHandler = getStartHandler(this.uniqid);\n\n if (!(await startHandler.isTinyAiDisabled())) {\n if(window.matchMedia(\"(pointer: coarse)\").matches) {\n // If we have a touch device, we need to manually trigger the tooltips by touching the cards.\n document.querySelectorAll('.tiny_ai-card-button.disabled').forEach(button => {\n button.parentElement.addEventListener(\n 'click', async() => {\n $(button).tooltip('toggle');\n });\n });\n }\n }\n\n if (summarizeButton) {\n summarizeButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('summarize');\n await this.renderer.renderSummarize();\n });\n }\n if (translateButton) {\n translateButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('translate');\n await this.renderer.renderTranslate();\n });\n }\n if (describeButton) {\n describeButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('describe');\n await this.renderer.renderDescribe();\n });\n }\n if (ttsButton) {\n ttsButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('tts');\n await this.renderer.renderTts();\n });\n }\n if (audiogenButton) {\n audiogenButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('audiogen');\n await this.renderer.renderAudiogen();\n });\n }\n if (imggenButton) {\n imggenButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('imggen');\n await this.renderer.renderImggen();\n });\n }\n if (describeimgButton) {\n describeimgButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('describeimg');\n await this.renderer.renderDescribeimg();\n });\n }\n if (imagetotextButton) {\n imagetotextButton.addEventListener('click', async() => {\n this.datamanager.setCurrentTool('imagetotext');\n await this.renderer.renderImagetotext();\n });\n }\n if (freePromptButton) {\n if (!freePromptButton.classList.contains('disabled')) {\n freePromptButton.addEventListener('click', async () => {\n this.datamanager.setCurrentTool('freeprompt');\n this.datamanager.setCurrentPrompt(this.baseElement.querySelector('[data-type=\"freepromptinput\"]').value);\n const result = await this.generateAiAnswer();\n if (result === null) {\n return;\n }\n await this.renderer.renderSuggestion();\n });\n } else {\n if (!(await startHandler.isTinyAiDisabled())) {\n freePromptButton.addEventListener('click', async() => {\n await errorAlert(startHandler.isToolDisabled('freeprompt', this.editorUtils.getMode()));\n });\n }\n }\n }\n }\n}\n"],"names":["BaseController","this","baseElement","summarizeButton","querySelector","translateButton","describeButton","ttsButton","audiogenButton","imggenButton","freePromptButton","describeimgButton","imagetotextButton","startHandler","uniqid","isTinyAiDisabled","window","matchMedia","matches","document","querySelectorAll","forEach","button","parentElement","addEventListener","async","tooltip","datamanager","setCurrentTool","renderer","renderSummarize","renderTranslate","renderDescribe","renderTts","renderAudiogen","renderImggen","renderDescribeimg","renderImagetotext","classList","contains","isToolDisabled","editorUtils","getMode","setCurrentPrompt","value","generateAiAnswer","renderSuggestion"],"mappings":";;;;;;;;wLA8B6BA,+BAGhBC,KAAKC,yBAIJC,gBAAkBF,KAAKC,YAAYE,cAAc,iCACjDC,gBAAkBJ,KAAKC,YAAYE,cAAc,iCACjDE,eAAiBL,KAAKC,YAAYE,cAAc,gCAChDG,UAAYN,KAAKC,YAAYE,cAAc,2BAC3CI,eAAiBP,KAAKC,YAAYE,cAAc,gCAChDK,aAAeR,KAAKC,YAAYE,cAAc,8BAC9CM,iBAAmBT,KAAKC,YAAYE,cAAc,kCAClDO,kBAAoBV,KAAKC,YAAYE,cAAc,mCACnDQ,kBAAoBX,KAAKC,YAAYE,cAAc,mCAEnDS,cAAe,0BAAgBZ,KAAKa,cAE9BD,aAAaE,oBAClBC,OAAOC,WAAW,qBAAqBC,SAEtCC,SAASC,iBAAiB,iCAAiCC,SAAQC,SAC/DA,OAAOC,cAAcC,iBACjB,SAASC,8BACHH,QAAQI,QAAQ,gBAMlCvB,iBACAA,gBAAgBqB,iBAAiB,SAASC,eACjCE,YAAYC,eAAe,mBAC1B3B,KAAK4B,SAASC,qBAGxBzB,iBACAA,gBAAgBmB,iBAAiB,SAASC,eACjCE,YAAYC,eAAe,mBAC1B3B,KAAK4B,SAASE,qBAGxBzB,gBACAA,eAAekB,iBAAiB,SAASC,eAChCE,YAAYC,eAAe,kBAC1B3B,KAAK4B,SAASG,oBAGxBzB,WACAA,UAAUiB,iBAAiB,SAASC,eAC3BE,YAAYC,eAAe,aAC1B3B,KAAK4B,SAASI,eAGxBzB,gBACAA,eAAegB,iBAAiB,SAASC,eAChCE,YAAYC,eAAe,kBAC1B3B,KAAK4B,SAASK,oBAGxBzB,cACAA,aAAae,iBAAiB,SAASC,eAC9BE,YAAYC,eAAe,gBAC1B3B,KAAK4B,SAASM,kBAGxBxB,mBACAA,kBAAkBa,iBAAiB,SAASC,eACnCE,YAAYC,eAAe,qBAC1B3B,KAAK4B,SAASO,uBAGxBxB,mBACAA,kBAAkBY,iBAAiB,SAASC,eACnCE,YAAYC,eAAe,qBAC1B3B,KAAK4B,SAASQ,uBAGxB3B,mBACKA,iBAAiB4B,UAAUC,SAAS,kBAWzB1B,aAAaE,oBACrBL,iBAAiBc,iBAAiB,SAASC,gBACjC,qBAAWZ,aAAa2B,eAAe,aAAcvC,KAAKwC,YAAYC,eAZpFhC,iBAAiBc,iBAAiB,SAASC,eAClCE,YAAYC,eAAe,mBAC3BD,YAAYgB,iBAAiB1C,KAAKC,YAAYE,cAAc,iCAAiCwC,OAEnF,aADM3C,KAAK4C,0BAIpB5C,KAAK4B,SAASiB"}
\ No newline at end of file
diff --git a/amd/build/datahandler/basedata.min.js b/amd/build/datahandler/basedata.min.js
index 849b95d..70a5c43 100644
--- a/amd/build/datahandler/basedata.min.js
+++ b/amd/build/datahandler/basedata.min.js
@@ -7,6 +7,6 @@ define("tiny_ai/datahandler/basedata",["exports","core/str","core/prefetch","tin
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-const stringKeys=["aigenerating","aisuggestion","audiogen_headline","audiogen_placeholder","back","backbutton_tooltip","cancel","deletebutton_tooltip","describe_baseprompt","describe_headline","dismiss","dismisssuggestion","error_nopromptgiven","freeprompt_placeholder","freepromptbutton_tooltip","gender","generalerror","generate","generatebutton_tooltip","hideprompt","imggen_headline","imggen_placeholder","insertatcaret","insertatcaretbutton_tooltip","insertbelow","insertbelowbutton_tooltip","keeplanguagetype","languagetype","languagetype_prompt","mainselection_heading","maxwordcount","maxwordcount_prompt","nomaxwordcount","regeneratebutton_tooltip","replaceselection","replaceselectionbutton_tooltip","reworkprompt","simplelanguage","size","showprompt","showpromptbutton_tooltip","summarize_baseprompt","summarize_headline","targetlanguage","technicallanguage","texttouse","toolname_audiogen","toolname_describe","toolname_describe_extension","toolname_imggen","toolname_summarize","toolname_summarize_extension","toolname_translate","toolname_translate_extension","toolname_tts","toolname_tts_extension","translate_baseprompt","translate_headline","tts_headline","voice"];let strings={};_exports.init=async()=>{(0,_prefetch.prefetchStrings)("tiny_ai",stringKeys);const stringRequest=stringKeys.map((key=>({key:key,component:"tiny_ai"})));[strings.aigenerating,strings.aisuggestion,strings.audiogen_headline,strings.audiogen_placeholder,strings.back,strings.backbutton_tooltip,strings.cancel,strings.deletebutton_tooltip,strings.describe_baseprompt,strings.describe_headline,strings.dismiss,strings.dismisssuggestion,strings.error_nopromptgiven,strings.freeprompt_placeholder,strings.freepromptbutton_tooltip,strings.gender,strings.generalerror,strings.generate,strings.generatebutton_tooltip,strings.hideprompt,strings.imggen_headline,strings.imggen_placeholder,strings.insertatcaret,strings.insertatcaretbutton_tooltip,strings.insertbelow,strings.insertbelowbutton_tooltip,strings.keeplanguagetype,strings.languagetype,strings.languagetype_prompt,strings.mainselection_heading,strings.maxwordcount,strings.maxwordcount_prompt,strings.nomaxwordcount,strings.regeneratebutton_tooltip,strings.replaceselection,strings.replaceselectionbutton_tooltip,strings.reworkprompt,strings.simplelanguage,strings.size,strings.showprompt,strings.showpromptbutton_tooltip,strings.summarize_baseprompt,strings.summarize_headline,strings.targetlanguage,strings.technicallanguage,strings.texttouse,strings.toolname_audiogen,strings.toolname_describe,strings.toolname_describe_extension,strings.toolname_imggen,strings.toolname_summarize,strings.toolname_summarize_extension,strings.toolname_translate,strings.toolname_translate_extension,strings.toolname_tts,strings.toolname_tts_extension,strings.translate_baseprompt,strings.translate_headline,strings.tts_headline,strings.voice]=await(0,_str.getStrings)(stringRequest)};_exports.getTinyAiString=string=>strings[string];_exports.getBackAndGenerateButtonContext=()=>({footer_buttons:[{hasText:!0,button_text:strings.back,icon_left:!0,icon_right:!1,primary:!1,secondary:!1,tertiary:!0,iconname:"arrow-left",iconstyle:"solid",action:"back",tooltip:strings.backbutton_tooltip},{hasText:!0,button_text:strings.generate,icon_left:!0,icon_right:!1,primary:!0,secondary:!1,tertiary:!1,iconname:"sparkle",customicon:!0,action:"generate",tooltip:strings.generatebutton_tooltip}]});_exports.getReplaceButtonsContext=mode=>({footer_iconbuttons:[{action:"delete",iconname:"trash",tooltip:strings.deletebutton_tooltip},{action:"regenerate",iconname:"arrows-rotate",tooltip:strings.regeneratebutton_tooltip}],footer_buttons:[{action:"insertbelow",hasText:!0,button_text:strings.insertbelow,icon_left:!0,icon_right:!1,secondary:!0,iconname:"text-insert-last",customicon:!0,tooltip:strings.insertbelow_tooltip},{action:mode===_constants.constants.modalModes.selection?"replace":"insertatcaret",hasText:!0,button_text:mode===_constants.constants.modalModes.selection?strings.replaceselection:strings.insertatcaret,icon_left:!0,icon_right:!1,primary:!0,iconname:"check",iconstyle:"solid",tooltip:mode===_constants.constants.modalModes.selection?strings.replaceselection_tooltip:strings.insertatcaret_tooltip}]});_exports.getInputContext=()=>({input:[{iconname:"sparkle",customicon:!0,button:[{customicon:!1,iconname:"arrow-right",iconstyle:"solid",icon_left:!1,icon_right:!0,tooltip:strings.freepromptbutton_tooltip}]}]});_exports.getShowPromptButtonContext=()=>({hasText:!0,button_text:strings.showprompt,icon_left:!0,icon_right:!1,tertiary:!0,iconname:"eye",iconstyle:"solid",action:"showprompt",textareatype:"prompt",collapsed:!0,tooltip:strings.showpromptbutton_tooltip})}));
+const stringKeys=["aigenerating","aisuggestion","audiogen_headline","audiogen_placeholder","back","backbutton_tooltip","cancel","deletebutton_tooltip","describeimg_baseprompt","describeimg_headline","describe_baseprompt","describe_headline","dismiss","dismisssuggestion","error_nofile","error_nofileinclipboard_text","error_nofileinclipboard_title","error_nopromptgiven","freeprompt_placeholder","freepromptbutton_tooltip","gender","generalerror","generate","generatebutton_tooltip","hideprompt","imagefromeditor","imagetotext_baseprompt","imagetotext_headline","imagetotext_insertimage","imggen_headline","imggen_placeholder","insertatcaret","insertatcaretbutton_tooltip","insertbelow","insertbelowbutton_tooltip","keeplanguagetype","languagetype","languagetype_prompt","mainselection_heading","maxwordcount","maxwordcount_prompt","nomaxwordcount","regeneratebutton_tooltip","replaceselection","replaceselectionbutton_tooltip","reworkprompt","simplelanguage","size","showprompt","showpromptbutton_tooltip","summarize_baseprompt","summarize_headline","targetlanguage","technicallanguage","texttouse","toolname_audiogen","toolname_describe","toolname_describeimg","toolname_describe_extension","toolname_imggen","toolname_imagetotext","toolname_summarize","toolname_summarize_extension","toolname_translate","toolname_translate_extension","toolname_tts","toolname_tts_extension","translate_baseprompt","translate_headline","tts_headline","voice"];let strings={};_exports.init=async()=>{(0,_prefetch.prefetchStrings)("tiny_ai",stringKeys);const stringRequest=stringKeys.map((key=>({key:key,component:"tiny_ai"})));[strings.aigenerating,strings.aisuggestion,strings.audiogen_headline,strings.audiogen_placeholder,strings.back,strings.backbutton_tooltip,strings.cancel,strings.deletebutton_tooltip,strings.describeimg_baseprompt,strings.describeimg_headline,strings.describe_baseprompt,strings.describe_headline,strings.dismiss,strings.dismisssuggestion,strings.error_nofile,strings.error_nofileinclipboard_text,strings.error_nofileinclipboard_title,strings.error_nopromptgiven,strings.freeprompt_placeholder,strings.freepromptbutton_tooltip,strings.gender,strings.generalerror,strings.generate,strings.generatebutton_tooltip,strings.hideprompt,strings.imagefromeditor,strings.imagetotext_baseprompt,strings.imagetotext_headline,strings.imagetotext_insertimage,strings.imggen_headline,strings.imggen_placeholder,strings.insertatcaret,strings.insertatcaretbutton_tooltip,strings.insertbelow,strings.insertbelowbutton_tooltip,strings.keeplanguagetype,strings.languagetype,strings.languagetype_prompt,strings.mainselection_heading,strings.maxwordcount,strings.maxwordcount_prompt,strings.nomaxwordcount,strings.regeneratebutton_tooltip,strings.replaceselection,strings.replaceselectionbutton_tooltip,strings.reworkprompt,strings.simplelanguage,strings.size,strings.showprompt,strings.showpromptbutton_tooltip,strings.summarize_baseprompt,strings.summarize_headline,strings.targetlanguage,strings.technicallanguage,strings.texttouse,strings.toolname_audiogen,strings.toolname_describe,strings.toolname_describeimg,strings.toolname_describe_extension,strings.toolname_imggen,strings.toolname_imagetotext,strings.toolname_summarize,strings.toolname_summarize_extension,strings.toolname_translate,strings.toolname_translate_extension,strings.toolname_tts,strings.toolname_tts_extension,strings.translate_baseprompt,strings.translate_headline,strings.tts_headline,strings.voice]=await(0,_str.getStrings)(stringRequest)};_exports.getTinyAiString=string=>strings[string];_exports.getBackAndGenerateButtonContext=()=>({footer_buttons:[{hasText:!0,button_text:strings.back,icon_left:!0,icon_right:!1,primary:!1,secondary:!1,tertiary:!0,iconname:"arrow-left",iconstyle:"solid",action:"back",tooltip:strings.backbutton_tooltip},{hasText:!0,button_text:strings.generate,icon_left:!0,icon_right:!1,primary:!0,secondary:!1,tertiary:!1,iconname:"sparkle",customicon:!0,action:"generate",tooltip:strings.generatebutton_tooltip}]});_exports.getReplaceButtonsContext=mode=>({footer_iconbuttons:[{action:"delete",iconname:"trash",tooltip:strings.deletebutton_tooltip},{action:"regenerate",iconname:"arrows-rotate",tooltip:strings.regeneratebutton_tooltip}],footer_buttons:[{action:"insertbelow",hasText:!0,button_text:strings.insertbelow,icon_left:!0,icon_right:!1,secondary:!0,iconname:"text-insert-last",customicon:!0,tooltip:strings.insertbelow_tooltip},{action:mode===_constants.constants.modalModes.selection?"replace":"insertatcaret",hasText:!0,button_text:mode===_constants.constants.modalModes.selection?strings.replaceselection:strings.insertatcaret,icon_left:!0,icon_right:!1,primary:!0,iconname:"check",iconstyle:"solid",tooltip:mode===_constants.constants.modalModes.selection?strings.replaceselection_tooltip:strings.insertatcaret_tooltip}]});_exports.getInputContext=()=>({input:[{iconname:"sparkle",customicon:!0,button:[{customicon:!1,iconname:"arrow-right",iconstyle:"solid",icon_left:!1,icon_right:!0,tooltip:strings.freepromptbutton_tooltip}]}]});_exports.getShowPromptButtonContext=()=>({hasText:!0,button_text:strings.showprompt,icon_left:!0,icon_right:!1,tertiary:!0,iconname:"eye",iconstyle:"solid",action:"showprompt",textareatype:"prompt",collapsed:!0,tooltip:strings.showpromptbutton_tooltip})}));
//# sourceMappingURL=basedata.min.js.map
\ No newline at end of file
diff --git a/amd/build/datahandler/basedata.min.js.map b/amd/build/datahandler/basedata.min.js.map
index a800db3..0878222 100644
--- a/amd/build/datahandler/basedata.min.js.map
+++ b/amd/build/datahandler/basedata.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"basedata.min.js","sources":["../../src/datahandler/basedata.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 .\n\nimport {getStrings} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {constants} from 'tiny_ai/constants';\n\n/**\n * Tiny AI base data provider.\n *\n * @module tiny_ai/datahandler/basedata\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst stringKeys = [\n 'aigenerating',\n 'aisuggestion',\n 'audiogen_headline',\n 'audiogen_placeholder',\n 'back',\n 'backbutton_tooltip',\n 'cancel',\n 'deletebutton_tooltip',\n 'describe_baseprompt',\n 'describe_headline',\n 'dismiss',\n 'dismisssuggestion',\n 'error_nopromptgiven',\n 'freeprompt_placeholder',\n 'freepromptbutton_tooltip',\n 'gender',\n 'generalerror',\n 'generate',\n 'generatebutton_tooltip',\n 'hideprompt',\n 'imggen_headline',\n 'imggen_placeholder',\n 'insertatcaret',\n 'insertatcaretbutton_tooltip',\n 'insertbelow',\n 'insertbelowbutton_tooltip',\n 'keeplanguagetype',\n 'languagetype',\n 'languagetype_prompt',\n 'mainselection_heading',\n 'maxwordcount',\n 'maxwordcount_prompt',\n 'nomaxwordcount',\n 'regeneratebutton_tooltip',\n 'replaceselection',\n 'replaceselectionbutton_tooltip',\n 'reworkprompt',\n 'simplelanguage',\n 'size',\n 'showprompt',\n 'showpromptbutton_tooltip',\n 'summarize_baseprompt',\n 'summarize_headline',\n 'targetlanguage',\n 'technicallanguage',\n 'texttouse',\n 'toolname_audiogen',\n 'toolname_describe',\n 'toolname_describe_extension',\n 'toolname_imggen',\n 'toolname_summarize',\n 'toolname_summarize_extension',\n 'toolname_translate',\n 'toolname_translate_extension',\n 'toolname_tts',\n 'toolname_tts_extension',\n 'translate_baseprompt',\n 'translate_headline',\n 'tts_headline',\n 'voice'\n];\n\nlet strings = {};\n\n\n\nexport const init = async() => {\n prefetchStrings('tiny_ai', stringKeys);\n const stringRequest = stringKeys.map(key => {\n return {key, component: 'tiny_ai'};\n });\n // We now get the strings. They are already prefetched, so this is not a performance feature.\n // We just use this to avoid having to code asynchronously all the time just for retrieving the\n // strings by using getString which returns a promise.\n [\n strings.aigenerating,\n strings.aisuggestion,\n strings.audiogen_headline,\n strings.audiogen_placeholder,\n strings.back,\n strings.backbutton_tooltip,\n strings.cancel,\n strings.deletebutton_tooltip,\n strings.describe_baseprompt,\n strings.describe_headline,\n strings.dismiss,\n strings.dismisssuggestion,\n strings.error_nopromptgiven,\n strings.freeprompt_placeholder,\n strings.freepromptbutton_tooltip,\n strings.gender,\n strings.generalerror,\n strings.generate,\n strings.generatebutton_tooltip,\n strings.hideprompt,\n strings.imggen_headline,\n strings.imggen_placeholder,\n strings.insertatcaret,\n strings.insertatcaretbutton_tooltip,\n strings.insertbelow,\n strings.insertbelowbutton_tooltip,\n strings.keeplanguagetype,\n strings.languagetype,\n strings.languagetype_prompt,\n strings.mainselection_heading,\n strings.maxwordcount,\n strings.maxwordcount_prompt,\n strings.nomaxwordcount,\n strings.regeneratebutton_tooltip,\n strings.replaceselection,\n strings.replaceselectionbutton_tooltip,\n strings.reworkprompt,\n strings.simplelanguage,\n strings.size,\n strings.showprompt,\n strings.showpromptbutton_tooltip,\n strings.summarize_baseprompt,\n strings.summarize_headline,\n strings.targetlanguage,\n strings.technicallanguage,\n strings.texttouse,\n strings.toolname_audiogen,\n strings.toolname_describe,\n strings.toolname_describe_extension,\n strings.toolname_imggen,\n strings.toolname_summarize,\n strings.toolname_summarize_extension,\n strings.toolname_translate,\n strings.toolname_translate_extension,\n strings.toolname_tts,\n strings.toolname_tts_extension,\n strings.translate_baseprompt,\n strings.translate_headline,\n strings.tts_headline,\n strings.voice\n ] = await getStrings(stringRequest);\n};\n\nexport const getTinyAiString = (string) => {\n return strings[string];\n};\n\nexport const getBackAndGenerateButtonContext = () => {\n return {\n footer_buttons: [\n {\n hasText: true,\n button_text: strings.back,\n icon_left: true,\n icon_right: false,\n primary: false,\n secondary: false,\n tertiary: true,\n iconname: 'arrow-left',\n iconstyle: 'solid',\n action: 'back',\n tooltip: strings.backbutton_tooltip\n },\n {\n hasText: true,\n button_text: strings.generate,\n icon_left: true,\n icon_right: false,\n primary: true,\n secondary: false,\n tertiary: false,\n iconname: 'sparkle',\n customicon: true,\n action: 'generate',\n tooltip: strings.generatebutton_tooltip\n }\n ]\n };\n};\n\nexport const getReplaceButtonsContext = (mode) => {\n\n return {\n footer_iconbuttons:\n [\n {\n action: 'delete',\n iconname: 'trash',\n tooltip: strings.deletebutton_tooltip\n },\n {\n action: 'regenerate',\n iconname: 'arrows-rotate',\n tooltip: strings.regeneratebutton_tooltip\n }\n ],\n footer_buttons:\n [\n {\n action: 'insertbelow',\n hasText: true,\n button_text: strings.insertbelow,\n icon_left: true,\n icon_right: false,\n secondary: true,\n iconname: 'text-insert-last',\n customicon: true,\n tooltip: strings.insertbelow_tooltip\n },\n {\n action: mode === constants.modalModes.selection ? 'replace' : 'insertatcaret',\n hasText: true,\n button_text: mode === constants.modalModes.selection ? strings.replaceselection : strings.insertatcaret,\n icon_left: true,\n icon_right: false,\n primary: true,\n iconname: 'check',\n iconstyle: 'solid',\n tooltip: mode === constants.modalModes.selection\n ? strings.replaceselection_tooltip : strings.insertatcaret_tooltip\n }\n ],\n };\n};\n\nexport const getInputContext = () => {\n return {\n input: [\n {\n iconname: 'sparkle',\n customicon: true,\n button: [\n {\n customicon: false,\n iconname: 'arrow-right',\n iconstyle: 'solid',\n icon_left: false,\n icon_right: true,\n tooltip: strings.freepromptbutton_tooltip\n }\n ]\n }\n ],\n };\n};\n\nexport const getShowPromptButtonContext = () => {\n return {\n hasText: true,\n button_text: strings.showprompt,\n icon_left: true,\n icon_right: false,\n tertiary: true,\n iconname: 'eye',\n iconstyle: 'solid',\n action: 'showprompt',\n textareatype: 'prompt',\n collapsed: true,\n tooltip: strings.showpromptbutton_tooltip\n };\n};\n"],"names":["stringKeys","strings","async","stringRequest","map","key","component","aigenerating","aisuggestion","audiogen_headline","audiogen_placeholder","back","backbutton_tooltip","cancel","deletebutton_tooltip","describe_baseprompt","describe_headline","dismiss","dismisssuggestion","error_nopromptgiven","freeprompt_placeholder","freepromptbutton_tooltip","gender","generalerror","generate","generatebutton_tooltip","hideprompt","imggen_headline","imggen_placeholder","insertatcaret","insertatcaretbutton_tooltip","insertbelow","insertbelowbutton_tooltip","keeplanguagetype","languagetype","languagetype_prompt","mainselection_heading","maxwordcount","maxwordcount_prompt","nomaxwordcount","regeneratebutton_tooltip","replaceselection","replaceselectionbutton_tooltip","reworkprompt","simplelanguage","size","showprompt","showpromptbutton_tooltip","summarize_baseprompt","summarize_headline","targetlanguage","technicallanguage","texttouse","toolname_audiogen","toolname_describe","toolname_describe_extension","toolname_imggen","toolname_summarize","toolname_summarize_extension","toolname_translate","toolname_translate_extension","toolname_tts","toolname_tts_extension","translate_baseprompt","translate_headline","tts_headline","voice","string","footer_buttons","hasText","button_text","icon_left","icon_right","primary","secondary","tertiary","iconname","iconstyle","action","tooltip","customicon","mode","footer_iconbuttons","insertbelow_tooltip","constants","modalModes","selection","replaceselection_tooltip","insertatcaret_tooltip","input","button","textareatype","collapsed"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"basedata.min.js","sources":["../../src/datahandler/basedata.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 .\n\nimport {getStrings} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {constants} from 'tiny_ai/constants';\n\n/**\n * Tiny AI base data provider.\n *\n * @module tiny_ai/datahandler/basedata\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst stringKeys = [\n 'aigenerating',\n 'aisuggestion',\n 'audiogen_headline',\n 'audiogen_placeholder',\n 'back',\n 'backbutton_tooltip',\n 'cancel',\n 'deletebutton_tooltip',\n 'describeimg_baseprompt',\n 'describeimg_headline',\n 'describe_baseprompt',\n 'describe_headline',\n 'dismiss',\n 'dismisssuggestion',\n 'error_nofile',\n 'error_nofileinclipboard_text',\n 'error_nofileinclipboard_title',\n 'error_nopromptgiven',\n 'freeprompt_placeholder',\n 'freepromptbutton_tooltip',\n 'gender',\n 'generalerror',\n 'generate',\n 'generatebutton_tooltip',\n 'hideprompt',\n 'imagefromeditor',\n 'imagetotext_baseprompt',\n 'imagetotext_headline',\n 'imagetotext_insertimage',\n 'imggen_headline',\n 'imggen_placeholder',\n 'insertatcaret',\n 'insertatcaretbutton_tooltip',\n 'insertbelow',\n 'insertbelowbutton_tooltip',\n 'keeplanguagetype',\n 'languagetype',\n 'languagetype_prompt',\n 'mainselection_heading',\n 'maxwordcount',\n 'maxwordcount_prompt',\n 'nomaxwordcount',\n 'regeneratebutton_tooltip',\n 'replaceselection',\n 'replaceselectionbutton_tooltip',\n 'reworkprompt',\n 'simplelanguage',\n 'size',\n 'showprompt',\n 'showpromptbutton_tooltip',\n 'summarize_baseprompt',\n 'summarize_headline',\n 'targetlanguage',\n 'technicallanguage',\n 'texttouse',\n 'toolname_audiogen',\n 'toolname_describe',\n 'toolname_describeimg',\n 'toolname_describe_extension',\n 'toolname_imggen',\n 'toolname_imagetotext',\n 'toolname_summarize',\n 'toolname_summarize_extension',\n 'toolname_translate',\n 'toolname_translate_extension',\n 'toolname_tts',\n 'toolname_tts_extension',\n 'translate_baseprompt',\n 'translate_headline',\n 'tts_headline',\n 'voice'\n];\n\nlet strings = {};\n\n\n\nexport const init = async() => {\n prefetchStrings('tiny_ai', stringKeys);\n const stringRequest = stringKeys.map(key => {\n return {key, component: 'tiny_ai'};\n });\n // We now get the strings. They are already prefetched, so this is not a performance feature.\n // We just use this to avoid having to code asynchronously all the time just for retrieving the\n // strings by using getString which returns a promise.\n [\n strings.aigenerating,\n strings.aisuggestion,\n strings.audiogen_headline,\n strings.audiogen_placeholder,\n strings.back,\n strings.backbutton_tooltip,\n strings.cancel,\n strings.deletebutton_tooltip,\n strings.describeimg_baseprompt,\n strings.describeimg_headline,\n strings.describe_baseprompt,\n strings.describe_headline,\n strings.dismiss,\n strings.dismisssuggestion,\n strings.error_nofile,\n strings.error_nofileinclipboard_text,\n strings.error_nofileinclipboard_title,\n strings.error_nopromptgiven,\n strings.freeprompt_placeholder,\n strings.freepromptbutton_tooltip,\n strings.gender,\n strings.generalerror,\n strings.generate,\n strings.generatebutton_tooltip,\n strings.hideprompt,\n strings.imagefromeditor,\n strings.imagetotext_baseprompt,\n strings.imagetotext_headline,\n strings.imagetotext_insertimage,\n strings.imggen_headline,\n strings.imggen_placeholder,\n strings.insertatcaret,\n strings.insertatcaretbutton_tooltip,\n strings.insertbelow,\n strings.insertbelowbutton_tooltip,\n strings.keeplanguagetype,\n strings.languagetype,\n strings.languagetype_prompt,\n strings.mainselection_heading,\n strings.maxwordcount,\n strings.maxwordcount_prompt,\n strings.nomaxwordcount,\n strings.regeneratebutton_tooltip,\n strings.replaceselection,\n strings.replaceselectionbutton_tooltip,\n strings.reworkprompt,\n strings.simplelanguage,\n strings.size,\n strings.showprompt,\n strings.showpromptbutton_tooltip,\n strings.summarize_baseprompt,\n strings.summarize_headline,\n strings.targetlanguage,\n strings.technicallanguage,\n strings.texttouse,\n strings.toolname_audiogen,\n strings.toolname_describe,\n strings.toolname_describeimg,\n strings.toolname_describe_extension,\n strings.toolname_imggen,\n strings.toolname_imagetotext,\n strings.toolname_summarize,\n strings.toolname_summarize_extension,\n strings.toolname_translate,\n strings.toolname_translate_extension,\n strings.toolname_tts,\n strings.toolname_tts_extension,\n strings.translate_baseprompt,\n strings.translate_headline,\n strings.tts_headline,\n strings.voice\n ] = await getStrings(stringRequest);\n};\n\nexport const getTinyAiString = (string) => {\n return strings[string];\n};\n\nexport const getBackAndGenerateButtonContext = () => {\n return {\n footer_buttons: [\n {\n hasText: true,\n button_text: strings.back,\n icon_left: true,\n icon_right: false,\n primary: false,\n secondary: false,\n tertiary: true,\n iconname: 'arrow-left',\n iconstyle: 'solid',\n action: 'back',\n tooltip: strings.backbutton_tooltip\n },\n {\n hasText: true,\n button_text: strings.generate,\n icon_left: true,\n icon_right: false,\n primary: true,\n secondary: false,\n tertiary: false,\n iconname: 'sparkle',\n customicon: true,\n action: 'generate',\n tooltip: strings.generatebutton_tooltip\n }\n ]\n };\n};\n\nexport const getReplaceButtonsContext = (mode) => {\n\n return {\n footer_iconbuttons:\n [\n {\n action: 'delete',\n iconname: 'trash',\n tooltip: strings.deletebutton_tooltip\n },\n {\n action: 'regenerate',\n iconname: 'arrows-rotate',\n tooltip: strings.regeneratebutton_tooltip\n }\n ],\n footer_buttons:\n [\n {\n action: 'insertbelow',\n hasText: true,\n button_text: strings.insertbelow,\n icon_left: true,\n icon_right: false,\n secondary: true,\n iconname: 'text-insert-last',\n customicon: true,\n tooltip: strings.insertbelow_tooltip\n },\n {\n action: mode === constants.modalModes.selection ? 'replace' : 'insertatcaret',\n hasText: true,\n button_text: mode === constants.modalModes.selection ? strings.replaceselection : strings.insertatcaret,\n icon_left: true,\n icon_right: false,\n primary: true,\n iconname: 'check',\n iconstyle: 'solid',\n tooltip: mode === constants.modalModes.selection\n ? strings.replaceselection_tooltip : strings.insertatcaret_tooltip\n }\n ],\n };\n};\n\nexport const getInputContext = () => {\n return {\n input: [\n {\n iconname: 'sparkle',\n customicon: true,\n button: [\n {\n customicon: false,\n iconname: 'arrow-right',\n iconstyle: 'solid',\n icon_left: false,\n icon_right: true,\n tooltip: strings.freepromptbutton_tooltip\n }\n ]\n }\n ],\n };\n};\n\nexport const getShowPromptButtonContext = () => {\n return {\n hasText: true,\n button_text: strings.showprompt,\n icon_left: true,\n icon_right: false,\n tertiary: true,\n iconname: 'eye',\n iconstyle: 'solid',\n action: 'showprompt',\n textareatype: 'prompt',\n collapsed: true,\n tooltip: strings.showpromptbutton_tooltip\n };\n};\n"],"names":["stringKeys","strings","async","stringRequest","map","key","component","aigenerating","aisuggestion","audiogen_headline","audiogen_placeholder","back","backbutton_tooltip","cancel","deletebutton_tooltip","describeimg_baseprompt","describeimg_headline","describe_baseprompt","describe_headline","dismiss","dismisssuggestion","error_nofile","error_nofileinclipboard_text","error_nofileinclipboard_title","error_nopromptgiven","freeprompt_placeholder","freepromptbutton_tooltip","gender","generalerror","generate","generatebutton_tooltip","hideprompt","imagefromeditor","imagetotext_baseprompt","imagetotext_headline","imagetotext_insertimage","imggen_headline","imggen_placeholder","insertatcaret","insertatcaretbutton_tooltip","insertbelow","insertbelowbutton_tooltip","keeplanguagetype","languagetype","languagetype_prompt","mainselection_heading","maxwordcount","maxwordcount_prompt","nomaxwordcount","regeneratebutton_tooltip","replaceselection","replaceselectionbutton_tooltip","reworkprompt","simplelanguage","size","showprompt","showpromptbutton_tooltip","summarize_baseprompt","summarize_headline","targetlanguage","technicallanguage","texttouse","toolname_audiogen","toolname_describe","toolname_describeimg","toolname_describe_extension","toolname_imggen","toolname_imagetotext","toolname_summarize","toolname_summarize_extension","toolname_translate","toolname_translate_extension","toolname_tts","toolname_tts_extension","translate_baseprompt","translate_headline","tts_headline","voice","string","footer_buttons","hasText","button_text","icon_left","icon_right","primary","secondary","tertiary","iconname","iconstyle","action","tooltip","customicon","mode","footer_iconbuttons","insertbelow_tooltip","constants","modalModes","selection","replaceselection_tooltip","insertatcaret_tooltip","input","button","textareatype","collapsed"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/datahandler/itt.min.js b/amd/build/datahandler/itt.min.js
new file mode 100644
index 0000000..3e117b9
--- /dev/null
+++ b/amd/build/datahandler/itt.min.js
@@ -0,0 +1,12 @@
+define("tiny_ai/datahandler/itt",["exports","local_ai_manager/config","tiny_ai/datahandler/basedata","tiny_ai/datahandler/base","tiny_ai/utils"],(function(_exports,AiConfig,BasedataHandler,_base,_utils){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,AiConfig=_interopRequireWildcard(AiConfig),BasedataHandler=_interopRequireWildcard(BasedataHandler),_base=(obj=_base)&&obj.__esModule?obj:{default:obj};
+/**
+ * Tiny AI data handler for image to text.
+ *
+ * @module tiny_ai/datahandler/itt
+ * @copyright 2024, ISB Bayern
+ * @author Philipp Memmel
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class _default extends _base.default{constructor(){super(...arguments),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"ittOptions",null)}async loadIttOptions(){if(null===this.ittOptions){const fetchedOptions=await AiConfig.getPurposeOptions("itt");this.ittOptions=JSON.parse(fetchedOptions.options)}}async getAllowedMimetypes(){return await this.loadIttOptions(),this.ittOptions.allowedmimetypes}getOptions(){const options={},datamanager=(0,_utils.getDatamanager)(this.uniqid);return options.image=datamanager.getCurrentFile(),options}getPrompt(tool){return BasedataHandler.getTinyAiString(tool+"_baseprompt")}async getTemplateContext(tool){const context={modal_headline:BasedataHandler.getTinyAiString(tool+"_headline"),showIcon:!0,tool:tool,textareatype:"prompt",placeholder:BasedataHandler.getTinyAiString(tool+"_placeholder"),insertimagedescription:BasedataHandler.getTinyAiString("imagetotext_insertimage")};return Object.assign(context,BasedataHandler.getShowPromptButtonContext()),Object.assign(context,BasedataHandler.getBackAndGenerateButtonContext()),context}}return _exports.default=_default,_exports.default}));
+
+//# sourceMappingURL=itt.min.js.map
\ No newline at end of file
diff --git a/amd/build/datahandler/itt.min.js.map b/amd/build/datahandler/itt.min.js.map
new file mode 100644
index 0000000..3a45fdc
--- /dev/null
+++ b/amd/build/datahandler/itt.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"itt.min.js","sources":["../../src/datahandler/itt.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 .\n\nimport * as AiConfig from 'local_ai_manager/config';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport BaseHandler from 'tiny_ai/datahandler/base';\nimport {getDatamanager} from 'tiny_ai/utils';\n\n/**\n * Tiny AI data handler for image to text.\n *\n * @module tiny_ai/datahandler/itt\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class extends BaseHandler {\n\n ittOptions = null;\n\n async loadIttOptions() {\n if (this.ittOptions === null) {\n const fetchedOptions = await AiConfig.getPurposeOptions('itt');\n this.ittOptions = JSON.parse(fetchedOptions.options);\n }\n }\n\n async getAllowedMimetypes() {\n await this.loadIttOptions();\n return this.ittOptions.allowedmimetypes;\n }\n\n getOptions() {\n const options = {};\n const datamanager = getDatamanager(this.uniqid);\n options.image = datamanager.getCurrentFile();\n return options;\n }\n\n /**\n * Get the prompt.\n *\n * @param {string} tool the tool to generate the prompt for, can be 'describeimage' and 'imagetotext'\n */\n getPrompt(tool) {\n return BasedataHandler.getTinyAiString(tool + '_baseprompt');\n }\n\n /**\n * Get the rendering context.\n *\n * @param {string} tool the tool to generate the context for, can be 'describeimage' and 'imagetotext'\n */\n async getTemplateContext(tool) {\n const context = {\n modal_headline: BasedataHandler.getTinyAiString(tool + '_headline'),\n showIcon: true,\n tool: tool,\n textareatype: 'prompt',\n placeholder: BasedataHandler.getTinyAiString(tool + '_placeholder'),\n insertimagedescription: BasedataHandler.getTinyAiString('imagetotext_insertimage')\n };\n\n Object.assign(context, BasedataHandler.getShowPromptButtonContext());\n\n Object.assign(context, BasedataHandler.getBackAndGenerateButtonContext());\n return context;\n }\n}\n"],"names":["BaseHandler","this","ittOptions","fetchedOptions","AiConfig","getPurposeOptions","JSON","parse","options","loadIttOptions","allowedmimetypes","getOptions","datamanager","uniqid","image","getCurrentFile","getPrompt","tool","BasedataHandler","getTinyAiString","context","modal_headline","showIcon","textareatype","placeholder","insertimagedescription","Object","assign","getShowPromptButtonContext","getBackAndGenerateButtonContext"],"mappings":";;;;;;;;;uBA6B6BA,4MAEZ,gCAGe,OAApBC,KAAKC,WAAqB,OACpBC,qBAAuBC,SAASC,kBAAkB,YACnDH,WAAaI,KAAKC,MAAMJ,eAAeK,mDAK1CP,KAAKQ,iBACJR,KAAKC,WAAWQ,iBAG3BC,mBACUH,QAAU,GACVI,aAAc,yBAAeX,KAAKY,eACxCL,QAAQM,MAAQF,YAAYG,iBACrBP,QAQXQ,UAAUC,aACCC,gBAAgBC,gBAAgBF,KAAO,wCAQzBA,YACfG,QAAU,CACZC,eAAgBH,gBAAgBC,gBAAgBF,KAAO,aACvDK,UAAU,EACVL,KAAMA,KACNM,aAAc,SACdC,YAAaN,gBAAgBC,gBAAgBF,KAAO,gBACpDQ,uBAAwBP,gBAAgBC,gBAAgB,mCAG5DO,OAAOC,OAAOP,QAASF,gBAAgBU,8BAEvCF,OAAOC,OAAOP,QAASF,gBAAgBW,mCAChCT"}
\ No newline at end of file
diff --git a/amd/build/datahandler/start.min.js b/amd/build/datahandler/start.min.js
index d78d1ec..fb7b5d7 100644
--- a/amd/build/datahandler/start.min.js
+++ b/amd/build/datahandler/start.min.js
@@ -6,6 +6,6 @@ define("tiny_ai/datahandler/start",["exports","core/config","core/str","tiny_ai/
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,config=_interopRequireWildcard(config),BasedataHandler=_interopRequireWildcard(BasedataHandler),_base=(obj=_base)&&obj.__esModule?obj:{default:obj};class _default extends _base.default{constructor(){super(...arguments),_defineProperty(this,"stringKeys",["error_limitreached","error_pleaseconfirm","error_purposenotconfigured","error_tenantdisabled","error_unavailable_noselection","error_unavailable_selection","error_userlocked","error_usernotconfirmed"]),_defineProperty(this,"aiConfig",null),_defineProperty(this,"strings",{})}async init(){this.aiConfig=await(0,_config2.getAiConfig)();const stringRequest=this.stringKeys.map((key=>({key:key,component:"local_ai_manager"})));[this.strings.error_limitreached,this.strings.error_pleaseconfirm,this.strings.error_purposenotconfigured,this.strings.error_tenantdisabled,this.strings.error_unavailable_noselection,this.strings.error_unavailable_selection,this.strings.error_userlocked,this.strings.error_usernotconfirmed]=await(0,_str.getStrings)(stringRequest),this.strings.error_editor_notavailable=await(0,_str.getString)("error_tiny_ai_notavailable","tiny_ai");const confirmLink=document.createElement("a");confirmLink.href="".concat(config.wwwroot,"/local/ai_manager/confirm_ai_usage.php"),confirmLink.innerText=this.strings.error_pleaseconfirm,confirmLink.target="_blank",this.strings.combinedusernotconfirmederror=this.strings.error_usernotconfirmed+" "+confirmLink.outerHTML}getPurposeConfig(tool){if(null===this.aiConfig)throw new Error("Coding error: init function was not called before accessing this.getPurposeConfig!");const toolPurpose=_constants.constants.toolPurposeMapping[tool];return this.aiConfig.purposes.filter((purpose=>purpose.purpose===toolPurpose))[0]}isTinyAiDisabled(){return this.aiConfig.tenantenabled?this.aiConfig.userconfirmed?this.aiConfig.userlocked?this.strings.error_userlocked:"":this.strings.combinedusernotconfirmederror:this.strings.error_tenantdisabled}isToolDisabled(tool,mode){if(this.isTinyAiDisabled())return this.isTinyAiDisabled();const purposeInfo=this.getPurposeConfig(tool);return purposeInfo.isconfigured?purposeInfo.limitreached?this.strings.error_limitreached:mode===_constants.constants.modalModes.selection?["audiogen","imggen"].includes(tool)?this.strings.error_unavailable_noselection:"":mode===_constants.constants.modalModes.general&&["summarize","translate","describe","tts"].includes(tool)?this.strings.error_unavailable_selection:"":this.strings.error_purposenotconfigured}isToolHidden(tool){const purposeInfo=this.getPurposeConfig(tool);if("role_basic"===this.aiConfig.role){if(!this.aiConfig.tenantenabled)return!0;if(!purposeInfo.isconfigured)return!0}return!1}async getTemplateContext(editorUtils){const mode=editorUtils.getMode();let toolButtons=[];"role_basic"===this.aiConfig.role&&this.isTinyAiDisabled()&&(await(0,_utils.errorAlert)(await(0,_str.getString)("error_tiny_ai_notavailable","tiny_ai")+"
"+this.isTinyAiDisabled()),editorUtils.getModal().destroy()),this.isToolHidden("summarize")||toolButtons.push({toolname:"summarize",tool:BasedataHandler.getTinyAiString("toolname_summarize"),description:BasedataHandler.getTinyAiString("toolname_summarize_extension"),customicon:!0,iconname:"shorten",disabled:this.isToolDisabled("summarize",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("summarize",mode)),action:"loadsummarize"}),this.isToolHidden("translate")||toolButtons.push({toolname:"translate",tool:BasedataHandler.getTinyAiString("toolname_translate"),description:BasedataHandler.getTinyAiString("toolname_translate_extension"),iconname:"language",iconstyle:"solid",disabled:this.isToolDisabled("translate",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("translate",mode)),action:"loadtranslate"}),this.isToolHidden("describe")||toolButtons.push({toolname:"describe",tool:BasedataHandler.getTinyAiString("toolname_describe"),description:BasedataHandler.getTinyAiString("toolname_describe_extension"),customicon:!0,iconname:"extend",disabled:this.isToolDisabled("describe",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("describe",mode)),action:"loaddescribe"}),this.isToolHidden("tts")||toolButtons.push({toolname:"tts",tool:BasedataHandler.getTinyAiString("toolname_tts"),description:BasedataHandler.getTinyAiString("toolname_tts_extension"),iconstyle:"solid",iconname:"microphone",disabled:this.isToolDisabled("tts",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("tts",mode)),action:"loadtts"}),this.isToolHidden("audiogen")||toolButtons.push({toolname:"audiogen",tool:BasedataHandler.getTinyAiString("toolname_audiogen"),iconstyle:"solid",iconname:"microphone",disabled:this.isToolDisabled("audiogen",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("audiogen",mode)),action:"loadaudiogen"}),this.isToolHidden("imggen")||toolButtons.push({toolname:"imggen",tool:BasedataHandler.getTinyAiString("toolname_imggen"),iconstyle:"solid",iconname:"image",disabled:this.isToolDisabled("imggen",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("imggen",mode)),action:"loadimggen"}),toolButtons.sort(((a,b)=>a.disabled&&!b.disabled?1:b.disabled&&!a.disabled?-1:0));const templateContext={showIcon:!0,modal_headline:BasedataHandler.getTinyAiString("mainselection_heading"),action:"loadfreeprompt",modal_buttons:toolButtons,freeprompthidden:!0};return Object.assign(templateContext,BasedataHandler.getInputContext()),this.isTinyAiDisabled()&&(templateContext.input[0].disabled=!0,templateContext.input[0].hasError=!0,templateContext.input[0].errorMessage=this.isTinyAiDisabled()),this.isToolDisabled("freeprompt",mode)&&(templateContext.input[0].disabled=!0),templateContext}}return _exports.default=_default,_exports.default}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,config=_interopRequireWildcard(config),BasedataHandler=_interopRequireWildcard(BasedataHandler),_base=(obj=_base)&&obj.__esModule?obj:{default:obj};class _default extends _base.default{constructor(){super(...arguments),_defineProperty(this,"stringKeys",["error_limitreached","error_pleaseconfirm","error_purposenotconfigured","error_tenantdisabled","error_unavailable_noselection","error_unavailable_selection","error_userlocked","error_usernotconfirmed"]),_defineProperty(this,"aiConfig",null),_defineProperty(this,"strings",{})}async init(){this.aiConfig=await(0,_config2.getAiConfig)();const stringRequest=this.stringKeys.map((key=>({key:key,component:"local_ai_manager"})));[this.strings.error_limitreached,this.strings.error_pleaseconfirm,this.strings.error_purposenotconfigured,this.strings.error_tenantdisabled,this.strings.error_unavailable_noselection,this.strings.error_unavailable_selection,this.strings.error_userlocked,this.strings.error_usernotconfirmed]=await(0,_str.getStrings)(stringRequest),this.strings.error_editor_notavailable=await(0,_str.getString)("error_tiny_ai_notavailable","tiny_ai");const confirmLink=document.createElement("a");confirmLink.href="".concat(config.wwwroot,"/local/ai_manager/confirm_ai_usage.php"),confirmLink.innerText=this.strings.error_pleaseconfirm,confirmLink.target="_blank",this.strings.combinedusernotconfirmederror=this.strings.error_usernotconfirmed+" "+confirmLink.outerHTML}getPurposeConfig(tool){if(null===this.aiConfig)throw new Error("Coding error: init function was not called before accessing this.getPurposeConfig!");const toolPurpose=_constants.constants.toolPurposeMapping[tool];return this.aiConfig.purposes.filter((purpose=>purpose.purpose===toolPurpose))[0]}isTinyAiDisabled(){return this.aiConfig.tenantenabled?this.aiConfig.userconfirmed?this.aiConfig.userlocked?this.strings.error_userlocked:"":this.strings.combinedusernotconfirmederror:this.strings.error_tenantdisabled}isToolDisabled(tool,mode){if(this.isTinyAiDisabled())return this.isTinyAiDisabled();const purposeInfo=this.getPurposeConfig(tool);return purposeInfo.isconfigured?purposeInfo.limitreached?this.strings.error_limitreached:mode===_constants.constants.modalModes.selection?["audiogen","imggen"].includes(tool)?this.strings.error_unavailable_noselection:"":mode===_constants.constants.modalModes.general&&["summarize","translate","describe","tts"].includes(tool)?this.strings.error_unavailable_selection:"":this.strings.error_purposenotconfigured}isToolHidden(tool){const purposeInfo=this.getPurposeConfig(tool);if("role_basic"===this.aiConfig.role){if(!this.aiConfig.tenantenabled)return!0;if(!purposeInfo.isconfigured)return!0}return!1}async getTemplateContext(editorUtils){const mode=editorUtils.getMode();let toolButtons=[];"role_basic"===this.aiConfig.role&&this.isTinyAiDisabled()&&(await(0,_utils.errorAlert)(await(0,_str.getString)("error_tiny_ai_notavailable","tiny_ai")+"
"+this.isTinyAiDisabled()),editorUtils.getModal().destroy()),this.isToolHidden("summarize")||toolButtons.push({toolname:"summarize",tool:BasedataHandler.getTinyAiString("toolname_summarize"),description:BasedataHandler.getTinyAiString("toolname_summarize_extension"),customicon:!0,iconname:"shorten",disabled:this.isToolDisabled("summarize",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("summarize",mode)),action:"loadsummarize"}),this.isToolHidden("translate")||toolButtons.push({toolname:"translate",tool:BasedataHandler.getTinyAiString("toolname_translate"),description:BasedataHandler.getTinyAiString("toolname_translate_extension"),iconname:"language",iconstyle:"solid",disabled:this.isToolDisabled("translate",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("translate",mode)),action:"loadtranslate"}),this.isToolHidden("describe")||toolButtons.push({toolname:"describe",tool:BasedataHandler.getTinyAiString("toolname_describe"),description:BasedataHandler.getTinyAiString("toolname_describe_extension"),customicon:!0,iconname:"extend",disabled:this.isToolDisabled("describe",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("describe",mode)),action:"loaddescribe"}),this.isToolHidden("tts")||toolButtons.push({toolname:"tts",tool:BasedataHandler.getTinyAiString("toolname_tts"),description:BasedataHandler.getTinyAiString("toolname_tts_extension"),iconstyle:"solid",iconname:"microphone",disabled:this.isToolDisabled("tts",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("tts",mode)),action:"loadtts"}),this.isToolHidden("audiogen")||toolButtons.push({toolname:"audiogen",tool:BasedataHandler.getTinyAiString("toolname_audiogen"),iconstyle:"solid",iconname:"microphone",disabled:this.isToolDisabled("audiogen",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("audiogen",mode)),action:"loadaudiogen"}),this.isToolHidden("imggen")||toolButtons.push({toolname:"imggen",tool:BasedataHandler.getTinyAiString("toolname_imggen"),iconstyle:"solid",iconname:"image",disabled:this.isToolDisabled("imggen",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("imggen",mode)),action:"loadimggen"}),this.isToolHidden("describeimg")||toolButtons.push({toolname:"describeimg",tool:BasedataHandler.getTinyAiString("toolname_describeimg"),iconstyle:"solid",iconname:"file-image",disabled:this.isToolDisabled("describeimg",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("describeimg",mode)),action:"loaddescribeimg"}),this.isToolHidden("imagetotext")||toolButtons.push({toolname:"imagetotext",tool:BasedataHandler.getTinyAiString("toolname_imagetotext"),iconstyle:"solid",iconname:"signature",disabled:this.isToolDisabled("imagetotext",mode).length>0,tooltip:(0,_utils.stripHtmlTags)(this.isToolDisabled("imagetotext",mode)),action:"loadimagetotext"}),toolButtons.sort(((a,b)=>a.disabled&&!b.disabled?1:b.disabled&&!a.disabled?-1:0));const templateContext={showIcon:!0,modal_headline:BasedataHandler.getTinyAiString("mainselection_heading"),action:"loadfreeprompt",modal_buttons:toolButtons,freeprompthidden:!0};return Object.assign(templateContext,BasedataHandler.getInputContext()),this.isTinyAiDisabled()&&(templateContext.input[0].disabled=!0,templateContext.input[0].hasError=!0,templateContext.input[0].errorMessage=this.isTinyAiDisabled()),this.isToolDisabled("freeprompt",mode)&&(templateContext.input[0].disabled=!0),templateContext}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=start.min.js.map
\ No newline at end of file
diff --git a/amd/build/datahandler/start.min.js.map b/amd/build/datahandler/start.min.js.map
index aa564f2..8f1e13a 100644
--- a/amd/build/datahandler/start.min.js.map
+++ b/amd/build/datahandler/start.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"start.min.js","sources":["../../src/datahandler/start.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 .\n\nimport * as config from 'core/config';\nimport {getString, getStrings} from 'core/str';\nimport {constants} from 'tiny_ai/constants';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport BaseHandler from 'tiny_ai/datahandler/base';\nimport {getAiConfig} from 'local_ai_manager/config';\nimport {errorAlert, stripHtmlTags} from 'tiny_ai/utils';\n\n\n/**\n * Tiny AI data handler for start page.\n *\n * @module tiny_ai/datahandler/start\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class extends BaseHandler {\n\n stringKeys = [\n 'error_limitreached',\n 'error_pleaseconfirm',\n 'error_purposenotconfigured',\n 'error_tenantdisabled',\n 'error_unavailable_noselection',\n 'error_unavailable_selection',\n 'error_userlocked',\n 'error_usernotconfirmed'\n ];\n\n aiConfig = null;\n strings = {};\n\n async init() {\n this.aiConfig = await getAiConfig();\n // It's easier to fetch alle these strings before even if we do not use them\n // instead of making all functions async just because of getString returning a promise.\n const stringRequest = this.stringKeys.map(key => {\n return {key, component: 'local_ai_manager'};\n });\n\n [\n this.strings.error_limitreached,\n this.strings.error_pleaseconfirm,\n this.strings.error_purposenotconfigured,\n this.strings.error_tenantdisabled,\n this.strings.error_unavailable_noselection,\n this.strings.error_unavailable_selection,\n this.strings.error_userlocked,\n this.strings.error_usernotconfirmed\n ] = await getStrings(stringRequest);\n this.strings.error_editor_notavailable = await getString('error_tiny_ai_notavailable', 'tiny_ai');\n const confirmLink = document.createElement('a');\n confirmLink.href = `${config.wwwroot}/local/ai_manager/confirm_ai_usage.php`;\n confirmLink.innerText = this.strings.error_pleaseconfirm;\n confirmLink.target = '_blank';\n this.strings.combinedusernotconfirmederror = this.strings.error_usernotconfirmed + ' ' + confirmLink.outerHTML;\n }\n\n getPurposeConfig(tool) {\n if (this.aiConfig === null) {\n throw new Error('Coding error: init function was not called before accessing this.getPurposeConfig!');\n }\n const toolPurpose = constants.toolPurposeMapping[tool];\n return this.aiConfig.purposes.filter(purpose => purpose['purpose'] === toolPurpose)[0];\n }\n\n isTinyAiDisabled() {\n if (!this.aiConfig.tenantenabled) {\n return this.strings.error_tenantdisabled;\n }\n if (!this.aiConfig.userconfirmed) {\n return this.strings.combinedusernotconfirmederror;\n }\n if (this.aiConfig.userlocked) {\n return this.strings.error_userlocked;\n }\n return '';\n }\n\n isToolDisabled(tool, mode) {\n if (this.isTinyAiDisabled()) {\n return this.isTinyAiDisabled();\n }\n const purposeInfo = this.getPurposeConfig(tool);\n if (!purposeInfo.isconfigured) {\n return this.strings.error_purposenotconfigured;\n }\n if (purposeInfo.limitreached) {\n return this.strings.error_limitreached;\n }\n\n if (mode === constants.modalModes.selection) {\n return ['audiogen', 'imggen'].includes(tool) ? this.strings.error_unavailable_noselection : '';\n } else if (mode === constants.modalModes.general) {\n return ['summarize', 'translate', 'describe', 'tts'].includes(tool) ? this.strings.error_unavailable_selection : '';\n }\n return '';\n }\n\n isToolHidden(tool) {\n const purposeInfo = this.getPurposeConfig(tool);\n // If the tenant is not allowed the plugin is being disabled completely, so we do not need\n // to check this case here.\n if (this.aiConfig.role === 'role_basic') {\n if (!this.aiConfig.tenantenabled) {\n return true;\n }\n if (!purposeInfo.isconfigured) {\n return true;\n }\n }\n return false;\n }\n\n async getTemplateContext(editorUtils) {\n const mode = editorUtils.getMode();\n let toolButtons = [];\n if (this.aiConfig.role === 'role_basic' && this.isTinyAiDisabled()) {\n await errorAlert(await getString('error_tiny_ai_notavailable', 'tiny_ai') + '
'\n + this.isTinyAiDisabled());\n editorUtils.getModal().destroy();\n }\n\n if (!this.isToolHidden('summarize')) {\n toolButtons.push({\n toolname: 'summarize',\n tool: BasedataHandler.getTinyAiString('toolname_summarize'),\n description: BasedataHandler.getTinyAiString('toolname_summarize_extension'),\n customicon: true,\n iconname: 'shorten',\n disabled: this.isToolDisabled('summarize', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('summarize', mode)),\n action: 'loadsummarize'\n });\n }\n if (!this.isToolHidden('translate')) {\n toolButtons.push({\n toolname: 'translate',\n tool: BasedataHandler.getTinyAiString('toolname_translate'),\n description: BasedataHandler.getTinyAiString('toolname_translate_extension'),\n iconname: 'language',\n iconstyle: 'solid',\n disabled: this.isToolDisabled('translate', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('translate', mode)),\n action: 'loadtranslate'\n });\n }\n if (!this.isToolHidden('describe')) {\n toolButtons.push({\n toolname: 'describe',\n tool: BasedataHandler.getTinyAiString('toolname_describe'),\n description: BasedataHandler.getTinyAiString('toolname_describe_extension'),\n customicon: true,\n iconname: 'extend',\n disabled: this.isToolDisabled('describe', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('describe', mode)),\n action: 'loaddescribe'\n });\n }\n if (!this.isToolHidden('tts')) {\n toolButtons.push({\n toolname: 'tts',\n tool: BasedataHandler.getTinyAiString('toolname_tts'),\n description: BasedataHandler.getTinyAiString('toolname_tts_extension'),\n iconstyle: 'solid',\n iconname: 'microphone',\n disabled: this.isToolDisabled('tts', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('tts', mode)),\n action: 'loadtts'\n });\n }\n if (!this.isToolHidden('audiogen')) {\n toolButtons.push({\n toolname: 'audiogen',\n tool: BasedataHandler.getTinyAiString('toolname_audiogen'),\n iconstyle: 'solid',\n iconname: 'microphone',\n disabled: this.isToolDisabled('audiogen', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('audiogen', mode)),\n action: 'loadaudiogen'\n });\n }\n if (!this.isToolHidden('imggen')) {\n toolButtons.push({\n toolname: 'imggen',\n tool: BasedataHandler.getTinyAiString('toolname_imggen'),\n iconstyle: 'solid',\n iconname: 'image',\n disabled: this.isToolDisabled('imggen', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('imggen', mode)),\n action: 'loadimggen'\n });\n }\n // We sort the not disabled tools to the top while keeping the groups \"disabled tools\" and \"not disabled tools\"\n // in the same order inside the groups.\n toolButtons.sort((a, b) => {\n if (a.disabled && !b.disabled) {\n return 1;\n } else if (b.disabled && !a.disabled) {\n return -1;\n } else {\n return 0;\n }\n });\n\n const templateContext = {\n showIcon: true,\n modal_headline: BasedataHandler.getTinyAiString('mainselection_heading'),\n action: 'loadfreeprompt',\n modal_buttons: toolButtons,\n freeprompthidden: true\n };\n Object.assign(templateContext, BasedataHandler.getInputContext());\n if (this.isTinyAiDisabled()) {\n templateContext.input[0].disabled = true;\n templateContext.input[0].hasError = true;\n templateContext.input[0].errorMessage = this.isTinyAiDisabled();\n }\n if (this.isToolDisabled('freeprompt', mode)) {\n templateContext.input[0].disabled = true;\n }\n return templateContext;\n }\n}\n"],"names":["BaseHandler","aiConfig","stringRequest","this","stringKeys","map","key","component","strings","error_limitreached","error_pleaseconfirm","error_purposenotconfigured","error_tenantdisabled","error_unavailable_noselection","error_unavailable_selection","error_userlocked","error_usernotconfirmed","error_editor_notavailable","confirmLink","document","createElement","href","config","wwwroot","innerText","target","combinedusernotconfirmederror","outerHTML","getPurposeConfig","tool","Error","toolPurpose","constants","toolPurposeMapping","purposes","filter","purpose","isTinyAiDisabled","tenantenabled","userconfirmed","userlocked","isToolDisabled","mode","purposeInfo","isconfigured","limitreached","modalModes","selection","includes","general","isToolHidden","role","editorUtils","getMode","toolButtons","getModal","destroy","push","toolname","BasedataHandler","getTinyAiString","description","customicon","iconname","disabled","length","tooltip","action","iconstyle","sort","a","b","templateContext","showIcon","modal_headline","modal_buttons","freeprompthidden","Object","assign","getInputContext","input","hasError","errorMessage"],"mappings":";;;;;;;;gQAiC6BA,kFAEZ,CACT,qBACA,sBACA,6BACA,uBACA,gCACA,8BACA,mBACA,2DAGO,qCACD,sBAGDC,eAAiB,gCAGhBC,cAAgBC,KAAKC,WAAWC,KAAIC,MAC/B,CAACA,IAAAA,IAAKC,UAAW,wBAIxBJ,KAAKK,QAAQC,mBACbN,KAAKK,QAAQE,oBACbP,KAAKK,QAAQG,2BACbR,KAAKK,QAAQI,qBACbT,KAAKK,QAAQK,8BACbV,KAAKK,QAAQM,4BACbX,KAAKK,QAAQO,iBACbZ,KAAKK,QAAQQ,8BACP,mBAAWd,oBAChBM,QAAQS,gCAAkC,kBAAU,6BAA8B,iBACjFC,YAAcC,SAASC,cAAc,KAC3CF,YAAYG,eAAUC,OAAOC,kDAC7BL,YAAYM,UAAYrB,KAAKK,QAAQE,oBACrCQ,YAAYO,OAAS,cAChBjB,QAAQkB,8BAAgCvB,KAAKK,QAAQQ,uBAAyB,IAAME,YAAYS,UAGzGC,iBAAiBC,SACS,OAAlB1B,KAAKF,eACC,IAAI6B,MAAM,4FAEdC,YAAcC,qBAAUC,mBAAmBJ,aAC1C1B,KAAKF,SAASiC,SAASC,QAAOC,SAAWA,QAAO,UAAgBL,cAAa,GAGxFM,0BACSlC,KAAKF,SAASqC,cAGdnC,KAAKF,SAASsC,cAGfpC,KAAKF,SAASuC,WACPrC,KAAKK,QAAQO,iBAEjB,GALIZ,KAAKK,QAAQkB,8BAHbvB,KAAKK,QAAQI,qBAW5B6B,eAAeZ,KAAMa,SACbvC,KAAKkC,0BACElC,KAAKkC,yBAEVM,YAAcxC,KAAKyB,iBAAiBC,aACrCc,YAAYC,aAGbD,YAAYE,aACL1C,KAAKK,QAAQC,mBAGpBiC,OAASV,qBAAUc,WAAWC,UACvB,CAAC,WAAY,UAAUC,SAASnB,MAAQ1B,KAAKK,QAAQK,8BAAgC,GACrF6B,OAASV,qBAAUc,WAAWG,SAC9B,CAAC,YAAa,YAAa,WAAY,OAAOD,SAASnB,MAAQ1B,KAAKK,QAAQM,4BAEhF,GAXIX,KAAKK,QAAQG,2BAc5BuC,aAAarB,YACHc,YAAcxC,KAAKyB,iBAAiBC,SAGf,eAAvB1B,KAAKF,SAASkD,KAAuB,KAChChD,KAAKF,SAASqC,qBACR,MAENK,YAAYC,oBACN,SAGR,2BAGcQ,mBACfV,KAAOU,YAAYC,cACrBC,YAAc,GACS,eAAvBnD,KAAKF,SAASkD,MAAyBhD,KAAKkC,2BACtC,2BAAiB,kBAAU,6BAA8B,WAAa,QACtElC,KAAKkC,oBACXe,YAAYG,WAAWC,WAGtBrD,KAAK+C,aAAa,cACnBI,YAAYG,KAAK,CACbC,SAAU,YACV7B,KAAM8B,gBAAgBC,gBAAgB,sBACtCC,YAAaF,gBAAgBC,gBAAgB,gCAC7CE,YAAY,EACZC,SAAU,UACVC,SAAU7D,KAAKsC,eAAe,YAAaC,MAAMuB,OAAS,EAC1DC,SAAS,wBAAc/D,KAAKsC,eAAe,YAAaC,OACxDyB,OAAQ,kBAGXhE,KAAK+C,aAAa,cACnBI,YAAYG,KAAK,CACbC,SAAU,YACV7B,KAAM8B,gBAAgBC,gBAAgB,sBACtCC,YAAaF,gBAAgBC,gBAAgB,gCAC7CG,SAAU,WACVK,UAAW,QACXJ,SAAU7D,KAAKsC,eAAe,YAAaC,MAAMuB,OAAS,EAC1DC,SAAS,wBAAc/D,KAAKsC,eAAe,YAAaC,OACxDyB,OAAQ,kBAGXhE,KAAK+C,aAAa,aACnBI,YAAYG,KAAK,CACbC,SAAU,WACV7B,KAAM8B,gBAAgBC,gBAAgB,qBACtCC,YAAaF,gBAAgBC,gBAAgB,+BAC7CE,YAAY,EACZC,SAAU,SACVC,SAAU7D,KAAKsC,eAAe,WAAYC,MAAMuB,OAAS,EACzDC,SAAS,wBAAc/D,KAAKsC,eAAe,WAAYC,OACvDyB,OAAQ,iBAGXhE,KAAK+C,aAAa,QACnBI,YAAYG,KAAK,CACbC,SAAU,MACV7B,KAAM8B,gBAAgBC,gBAAgB,gBACtCC,YAAaF,gBAAgBC,gBAAgB,0BAC7CQ,UAAW,QACXL,SAAU,aACVC,SAAU7D,KAAKsC,eAAe,MAAOC,MAAMuB,OAAS,EACpDC,SAAS,wBAAc/D,KAAKsC,eAAe,MAAOC,OAClDyB,OAAQ,YAGXhE,KAAK+C,aAAa,aACnBI,YAAYG,KAAK,CACbC,SAAU,WACV7B,KAAM8B,gBAAgBC,gBAAgB,qBACtCQ,UAAW,QACXL,SAAU,aACVC,SAAU7D,KAAKsC,eAAe,WAAYC,MAAMuB,OAAS,EACzDC,SAAS,wBAAc/D,KAAKsC,eAAe,WAAYC,OACvDyB,OAAQ,iBAGXhE,KAAK+C,aAAa,WACnBI,YAAYG,KAAK,CACbC,SAAU,SACV7B,KAAM8B,gBAAgBC,gBAAgB,mBACtCQ,UAAW,QACXL,SAAU,QACVC,SAAU7D,KAAKsC,eAAe,SAAUC,MAAMuB,OAAS,EACvDC,SAAS,wBAAc/D,KAAKsC,eAAe,SAAUC,OACrDyB,OAAQ,eAKhBb,YAAYe,MAAK,CAACC,EAAGC,IACbD,EAAEN,WAAaO,EAAEP,SACV,EACAO,EAAEP,WAAaM,EAAEN,UAChB,EAED,UAITQ,gBAAkB,CACpBC,UAAU,EACVC,eAAgBf,gBAAgBC,gBAAgB,yBAChDO,OAAQ,iBACRQ,cAAerB,YACfsB,kBAAkB,UAEtBC,OAAOC,OAAON,gBAAiBb,gBAAgBoB,mBAC3C5E,KAAKkC,qBACLmC,gBAAgBQ,MAAM,GAAGhB,UAAW,EACpCQ,gBAAgBQ,MAAM,GAAGC,UAAW,EACpCT,gBAAgBQ,MAAM,GAAGE,aAAe/E,KAAKkC,oBAE7ClC,KAAKsC,eAAe,aAAcC,QAClC8B,gBAAgBQ,MAAM,GAAGhB,UAAW,GAEjCQ"}
\ No newline at end of file
+{"version":3,"file":"start.min.js","sources":["../../src/datahandler/start.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 .\n\nimport * as config from 'core/config';\nimport {getString, getStrings} from 'core/str';\nimport {constants} from 'tiny_ai/constants';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport BaseHandler from 'tiny_ai/datahandler/base';\nimport {getAiConfig} from 'local_ai_manager/config';\nimport {errorAlert, stripHtmlTags} from 'tiny_ai/utils';\n\n\n/**\n * Tiny AI data handler for start page.\n *\n * @module tiny_ai/datahandler/start\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class extends BaseHandler {\n\n stringKeys = [\n 'error_limitreached',\n 'error_pleaseconfirm',\n 'error_purposenotconfigured',\n 'error_tenantdisabled',\n 'error_unavailable_noselection',\n 'error_unavailable_selection',\n 'error_userlocked',\n 'error_usernotconfirmed'\n ];\n\n aiConfig = null;\n strings = {};\n\n async init() {\n this.aiConfig = await getAiConfig();\n // It's easier to fetch alle these strings before even if we do not use them\n // instead of making all functions async just because of getString returning a promise.\n const stringRequest = this.stringKeys.map(key => {\n return {key, component: 'local_ai_manager'};\n });\n\n [\n this.strings.error_limitreached,\n this.strings.error_pleaseconfirm,\n this.strings.error_purposenotconfigured,\n this.strings.error_tenantdisabled,\n this.strings.error_unavailable_noselection,\n this.strings.error_unavailable_selection,\n this.strings.error_userlocked,\n this.strings.error_usernotconfirmed\n ] = await getStrings(stringRequest);\n this.strings.error_editor_notavailable = await getString('error_tiny_ai_notavailable', 'tiny_ai');\n const confirmLink = document.createElement('a');\n confirmLink.href = `${config.wwwroot}/local/ai_manager/confirm_ai_usage.php`;\n confirmLink.innerText = this.strings.error_pleaseconfirm;\n confirmLink.target = '_blank';\n this.strings.combinedusernotconfirmederror = this.strings.error_usernotconfirmed + ' ' + confirmLink.outerHTML;\n }\n\n getPurposeConfig(tool) {\n if (this.aiConfig === null) {\n throw new Error('Coding error: init function was not called before accessing this.getPurposeConfig!');\n }\n const toolPurpose = constants.toolPurposeMapping[tool];\n return this.aiConfig.purposes.filter(purpose => purpose['purpose'] === toolPurpose)[0];\n }\n\n isTinyAiDisabled() {\n if (!this.aiConfig.tenantenabled) {\n return this.strings.error_tenantdisabled;\n }\n if (!this.aiConfig.userconfirmed) {\n return this.strings.combinedusernotconfirmederror;\n }\n if (this.aiConfig.userlocked) {\n return this.strings.error_userlocked;\n }\n return '';\n }\n\n isToolDisabled(tool, mode) {\n if (this.isTinyAiDisabled()) {\n return this.isTinyAiDisabled();\n }\n const purposeInfo = this.getPurposeConfig(tool);\n if (!purposeInfo.isconfigured) {\n return this.strings.error_purposenotconfigured;\n }\n if (purposeInfo.limitreached) {\n return this.strings.error_limitreached;\n }\n\n if (mode === constants.modalModes.selection) {\n return ['audiogen', 'imggen']\n .includes(tool) ? this.strings.error_unavailable_noselection : '';\n } else if (mode === constants.modalModes.general) {\n return ['summarize', 'translate', 'describe', 'tts']\n .includes(tool) ? this.strings.error_unavailable_selection : '';\n }\n return '';\n }\n\n isToolHidden(tool) {\n const purposeInfo = this.getPurposeConfig(tool);\n // If the tenant is not allowed the plugin is being disabled completely, so we do not need\n // to check this case here.\n if (this.aiConfig.role === 'role_basic') {\n if (!this.aiConfig.tenantenabled) {\n return true;\n }\n if (!purposeInfo.isconfigured) {\n return true;\n }\n }\n return false;\n }\n\n async getTemplateContext(editorUtils) {\n const mode = editorUtils.getMode();\n let toolButtons = [];\n if (this.aiConfig.role === 'role_basic' && this.isTinyAiDisabled()) {\n await errorAlert(await getString('error_tiny_ai_notavailable', 'tiny_ai') + '
'\n + this.isTinyAiDisabled());\n editorUtils.getModal().destroy();\n }\n\n if (!this.isToolHidden('summarize')) {\n toolButtons.push({\n toolname: 'summarize',\n tool: BasedataHandler.getTinyAiString('toolname_summarize'),\n description: BasedataHandler.getTinyAiString('toolname_summarize_extension'),\n customicon: true,\n iconname: 'shorten',\n disabled: this.isToolDisabled('summarize', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('summarize', mode)),\n action: 'loadsummarize'\n });\n }\n if (!this.isToolHidden('translate')) {\n toolButtons.push({\n toolname: 'translate',\n tool: BasedataHandler.getTinyAiString('toolname_translate'),\n description: BasedataHandler.getTinyAiString('toolname_translate_extension'),\n iconname: 'language',\n iconstyle: 'solid',\n disabled: this.isToolDisabled('translate', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('translate', mode)),\n action: 'loadtranslate'\n });\n }\n if (!this.isToolHidden('describe')) {\n toolButtons.push({\n toolname: 'describe',\n tool: BasedataHandler.getTinyAiString('toolname_describe'),\n description: BasedataHandler.getTinyAiString('toolname_describe_extension'),\n customicon: true,\n iconname: 'extend',\n disabled: this.isToolDisabled('describe', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('describe', mode)),\n action: 'loaddescribe'\n });\n }\n if (!this.isToolHidden('tts')) {\n toolButtons.push({\n toolname: 'tts',\n tool: BasedataHandler.getTinyAiString('toolname_tts'),\n description: BasedataHandler.getTinyAiString('toolname_tts_extension'),\n iconstyle: 'solid',\n iconname: 'microphone',\n disabled: this.isToolDisabled('tts', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('tts', mode)),\n action: 'loadtts'\n });\n }\n if (!this.isToolHidden('audiogen')) {\n toolButtons.push({\n toolname: 'audiogen',\n tool: BasedataHandler.getTinyAiString('toolname_audiogen'),\n iconstyle: 'solid',\n iconname: 'microphone',\n disabled: this.isToolDisabled('audiogen', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('audiogen', mode)),\n action: 'loadaudiogen'\n });\n }\n if (!this.isToolHidden('imggen')) {\n toolButtons.push({\n toolname: 'imggen',\n tool: BasedataHandler.getTinyAiString('toolname_imggen'),\n iconstyle: 'solid',\n iconname: 'image',\n disabled: this.isToolDisabled('imggen', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('imggen', mode)),\n action: 'loadimggen'\n });\n }\n if (!this.isToolHidden('describeimg')) {\n toolButtons.push({\n toolname: 'describeimg',\n tool: BasedataHandler.getTinyAiString('toolname_describeimg'),\n iconstyle: 'solid',\n iconname: 'file-image',\n disabled: this.isToolDisabled('describeimg', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('describeimg', mode)),\n action: 'loaddescribeimg'\n });\n }\n if (!this.isToolHidden('imagetotext')) {\n toolButtons.push({\n toolname: 'imagetotext',\n tool: BasedataHandler.getTinyAiString('toolname_imagetotext'),\n iconstyle: 'solid',\n iconname: 'signature',\n disabled: this.isToolDisabled('imagetotext', mode).length > 0,\n tooltip: stripHtmlTags(this.isToolDisabled('imagetotext', mode)),\n action: 'loadimagetotext'\n });\n }\n // We sort the not disabled tools to the top while keeping the groups \"disabled tools\" and \"not disabled tools\"\n // in the same order inside the groups.\n toolButtons.sort((a, b) => {\n if (a.disabled && !b.disabled) {\n return 1;\n } else if (b.disabled && !a.disabled) {\n return -1;\n } else {\n return 0;\n }\n });\n\n const templateContext = {\n showIcon: true,\n modal_headline: BasedataHandler.getTinyAiString('mainselection_heading'),\n action: 'loadfreeprompt',\n modal_buttons: toolButtons,\n freeprompthidden: true\n };\n Object.assign(templateContext, BasedataHandler.getInputContext());\n if (this.isTinyAiDisabled()) {\n templateContext.input[0].disabled = true;\n templateContext.input[0].hasError = true;\n templateContext.input[0].errorMessage = this.isTinyAiDisabled();\n }\n if (this.isToolDisabled('freeprompt', mode)) {\n templateContext.input[0].disabled = true;\n }\n return templateContext;\n }\n}\n"],"names":["BaseHandler","aiConfig","stringRequest","this","stringKeys","map","key","component","strings","error_limitreached","error_pleaseconfirm","error_purposenotconfigured","error_tenantdisabled","error_unavailable_noselection","error_unavailable_selection","error_userlocked","error_usernotconfirmed","error_editor_notavailable","confirmLink","document","createElement","href","config","wwwroot","innerText","target","combinedusernotconfirmederror","outerHTML","getPurposeConfig","tool","Error","toolPurpose","constants","toolPurposeMapping","purposes","filter","purpose","isTinyAiDisabled","tenantenabled","userconfirmed","userlocked","isToolDisabled","mode","purposeInfo","isconfigured","limitreached","modalModes","selection","includes","general","isToolHidden","role","editorUtils","getMode","toolButtons","getModal","destroy","push","toolname","BasedataHandler","getTinyAiString","description","customicon","iconname","disabled","length","tooltip","action","iconstyle","sort","a","b","templateContext","showIcon","modal_headline","modal_buttons","freeprompthidden","Object","assign","getInputContext","input","hasError","errorMessage"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/datamanager.min.js b/amd/build/datamanager.min.js
index f005bee..56b5e61 100644
--- a/amd/build/datamanager.min.js
+++ b/amd/build/datamanager.min.js
@@ -6,6 +6,6 @@ define("tiny_ai/datamanager",["exports","tiny_ai/utils"],(function(_exports,_uti
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default=class{constructor(uniqid){_defineProperty(this,"uniqid",null),_defineProperty(this,"currentTool",null),_defineProperty(this,"currentAiResult",null),_defineProperty(this,"prompt",null),_defineProperty(this,"options",null),this.uniqid=uniqid,this.eventEmitterElement=document.createElement("div")}getDefaultOptions(){const defaultOptions={component:"tiny_ai",itemid:(0,_utils.getEditorUtils)(this.uniqid).getDraftItemId(),contextid:(0,_utils.getEditorUtils)(this.uniqid).getContextId()};return["tts","audiogen"].includes(this.getCurrentTool())?defaultOptions.filename="audio_"+Math.random().toString(16).slice(2)+".mp3":"imggen"===this.getCurrentTool()&&(defaultOptions.filename="img_"+Math.random().toString(16).slice(2)+".png"),defaultOptions}setCurrentTool(currentTool){this.currentTool=currentTool}getCurrentTool(){return this.currentTool}setCurrentPrompt(prompt){this.prompt=prompt;const promptUpdatedEvent=new CustomEvent("promptUpdated",{detail:{newPrompt:prompt}});this.eventEmitterElement.dispatchEvent(promptUpdatedEvent)}getCurrentPrompt(){return this.prompt}getSelection(){return this.selection}getSelectionText(){const span=document.createElement("span");return span.innerHTML=this.selection,span.textContent}setSelection(selection){this.selection=selection}getEventEmitterElement(){return this.eventEmitterElement}setCurrentAiResult(aiResult){this.currentAiResult=aiResult}getCurrentAiResult(){return this.currentAiResult}setCurrentOptions(options){this.options=options}getCurrentOptions(){const optionsToReturn=null===this.options?{}:this.options;return Object.assign(optionsToReturn,this.getDefaultOptions()),optionsToReturn}reset(){this.setCurrentPrompt(""),this.setCurrentOptions(null),this.setCurrentTool(null),this.setCurrentAiResult(null)}},_exports.default}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default=class{constructor(uniqid){_defineProperty(this,"uniqid",null),_defineProperty(this,"currentTool",null),_defineProperty(this,"currentAiResult",null),_defineProperty(this,"prompt",null),_defineProperty(this,"file",null),_defineProperty(this,"options",null),_defineProperty(this,"selection",null),_defineProperty(this,"selectionImg",null),this.uniqid=uniqid,this.eventEmitterElement=document.createElement("div")}getDefaultOptions(){const defaultOptions={component:"tiny_ai",itemid:(0,_utils.getEditorUtils)(this.uniqid).getDraftItemId(),contextid:(0,_utils.getEditorUtils)(this.uniqid).getContextId()};return["tts","audiogen"].includes(this.getCurrentTool())?defaultOptions.filename="audio_"+Math.random().toString(16).slice(2)+".mp3":"imggen"===this.getCurrentTool()&&(defaultOptions.filename="img_"+Math.random().toString(16).slice(2)+".png"),defaultOptions}setCurrentTool(currentTool){this.currentTool=currentTool}getCurrentTool(){return this.currentTool}setCurrentPrompt(prompt){this.prompt=prompt;const promptUpdatedEvent=new CustomEvent("promptUpdated",{detail:{newPrompt:prompt}});this.eventEmitterElement.dispatchEvent(promptUpdatedEvent)}getCurrentPrompt(){return this.prompt}setCurrentFile(file){this.file=file}getCurrentFile(){return this.file}getSelection(){return this.selection}getSelectionText(){const span=document.createElement("span");return span.innerHTML=this.selection,span.textContent}setSelection(selection){this.selection=selection}getSelectionImg(){return this.selectionImg}setSelectionImg(image){this.selectionImg=image}getEventEmitterElement(){return this.eventEmitterElement}setCurrentAiResult(aiResult){this.currentAiResult=aiResult}getCurrentAiResult(){return this.currentAiResult}setCurrentOptions(options){this.options=options}getCurrentOptions(){const optionsToReturn=null===this.options?{}:this.options;return Object.assign(optionsToReturn,this.getDefaultOptions()),optionsToReturn}reset(){this.setCurrentPrompt(""),this.setCurrentOptions(null),this.setCurrentTool(null),this.setCurrentAiResult(null),this.setCurrentFile(null)}},_exports.default}));
//# sourceMappingURL=datamanager.min.js.map
\ No newline at end of file
diff --git a/amd/build/datamanager.min.js.map b/amd/build/datamanager.min.js.map
index 7e45559..ece3fc1 100644
--- a/amd/build/datamanager.min.js.map
+++ b/amd/build/datamanager.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"datamanager.min.js","sources":["../src/datamanager.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 .\n\nimport {getEditorUtils} from 'tiny_ai/utils';\n\n/**\n * Tiny AI data manager.\n *\n * @module tiny_ai/datamanager\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class DataManager {\n\n uniqid = null;\n\n constructor(uniqid) {\n this.uniqid = uniqid;\n this.eventEmitterElement = document.createElement('div');\n }\n\n currentTool = null;\n currentAiResult = null;\n prompt = null;\n options = null;\n\n getDefaultOptions() {\n const defaultOptions = {\n component: 'tiny_ai',\n itemid: getEditorUtils(this.uniqid).getDraftItemId(),\n contextid: getEditorUtils(this.uniqid).getContextId()\n };\n if (['tts', 'audiogen'].includes(this.getCurrentTool())) {\n defaultOptions.filename = 'audio_' + Math.random().toString(16).slice(2) + '.mp3';\n } else if (this.getCurrentTool() === 'imggen') {\n defaultOptions.filename = 'img_' + Math.random().toString(16).slice(2) + '.png';\n }\n return defaultOptions;\n }\n\n setCurrentTool(currentTool) {\n this.currentTool = currentTool;\n }\n\n getCurrentTool() {\n return this.currentTool;\n }\n\n setCurrentPrompt(prompt) {\n this.prompt = prompt;\n const promptUpdatedEvent = new CustomEvent('promptUpdated', {\n detail: {\n newPrompt: prompt\n }\n });\n this.eventEmitterElement.dispatchEvent(promptUpdatedEvent);\n }\n\n getCurrentPrompt() {\n return this.prompt;\n\n }\n\n getSelection() {\n return this.selection;\n }\n\n getSelectionText() {\n const span = document.createElement('span');\n span.innerHTML = this.selection;\n return span.textContent;\n }\n\n setSelection(selection) {\n this.selection = selection;\n }\n\n getEventEmitterElement() {\n return this.eventEmitterElement;\n }\n\n setCurrentAiResult(aiResult) {\n this.currentAiResult = aiResult;\n }\n\n getCurrentAiResult() {\n return this.currentAiResult;\n }\n\n setCurrentOptions(options) {\n this.options = options;\n }\n\n getCurrentOptions() {\n const optionsToReturn = this.options === null ? {} : this.options;\n Object.assign(optionsToReturn, this.getDefaultOptions());\n return optionsToReturn;\n }\n\n reset() {\n this.setCurrentPrompt('');\n this.setCurrentOptions(null);\n this.setCurrentTool(null);\n this.setCurrentAiResult(null);\n }\n}\n\n\n\n\n"],"names":["constructor","uniqid","eventEmitterElement","document","createElement","getDefaultOptions","defaultOptions","component","itemid","this","getDraftItemId","contextid","getContextId","includes","getCurrentTool","filename","Math","random","toString","slice","setCurrentTool","currentTool","setCurrentPrompt","prompt","promptUpdatedEvent","CustomEvent","detail","newPrompt","dispatchEvent","getCurrentPrompt","getSelection","selection","getSelectionText","span","innerHTML","textContent","setSelection","getEventEmitterElement","setCurrentAiResult","aiResult","currentAiResult","getCurrentAiResult","setCurrentOptions","options","getCurrentOptions","optionsToReturn","Object","assign","reset"],"mappings":";;;;;;;;mHA8BIA,YAAYC,sCAFH,yCAOK,6CACI,oCACT,qCACC,WAPDA,OAASA,YACTC,oBAAsBC,SAASC,cAAc,OAQtDC,0BACUC,eAAiB,CACnBC,UAAW,UACXC,QAAQ,yBAAeC,KAAKR,QAAQS,iBACpCC,WAAW,yBAAeF,KAAKR,QAAQW,sBAEvC,CAAC,MAAO,YAAYC,SAASJ,KAAKK,kBAClCR,eAAeS,SAAW,SAAWC,KAAKC,SAASC,SAAS,IAAIC,MAAM,GAAK,OAC1C,WAA1BV,KAAKK,mBACZR,eAAeS,SAAW,OAASC,KAAKC,SAASC,SAAS,IAAIC,MAAM,GAAK,QAEtEb,eAGXc,eAAeC,kBACNA,YAAcA,YAGvBP,wBACWL,KAAKY,YAGhBC,iBAAiBC,aACRA,OAASA,aACRC,mBAAqB,IAAIC,YAAY,gBAAiB,CACxDC,OAAQ,CACJC,UAAWJ,eAGdrB,oBAAoB0B,cAAcJ,oBAG3CK,0BACWpB,KAAKc,OAIhBO,sBACWrB,KAAKsB,UAGhBC,yBACUC,KAAO9B,SAASC,cAAc,eACpC6B,KAAKC,UAAYzB,KAAKsB,UACfE,KAAKE,YAGhBC,aAAaL,gBACJA,UAAYA,UAGrBM,gCACW5B,KAAKP,oBAGhBoC,mBAAmBC,eACVC,gBAAkBD,SAG3BE,4BACWhC,KAAK+B,gBAGhBE,kBAAkBC,cACTA,QAAUA,QAGnBC,0BACUC,gBAAmC,OAAjBpC,KAAKkC,QAAmB,GAAKlC,KAAKkC,eAC1DG,OAAOC,OAAOF,gBAAiBpC,KAAKJ,qBAC7BwC,gBAGXG,aACS1B,iBAAiB,SACjBoB,kBAAkB,WAClBtB,eAAe,WACfkB,mBAAmB"}
\ No newline at end of file
+{"version":3,"file":"datamanager.min.js","sources":["../src/datamanager.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 .\n\nimport {getEditorUtils} from 'tiny_ai/utils';\n\n/**\n * Tiny AI data manager.\n *\n * @module tiny_ai/datamanager\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class DataManager {\n\n uniqid = null;\n\n constructor(uniqid) {\n this.uniqid = uniqid;\n this.eventEmitterElement = document.createElement('div');\n }\n\n currentTool = null;\n currentAiResult = null;\n prompt = null;\n file = null;\n options = null;\n selection = null;\n selectionImg = null;\n\n getDefaultOptions() {\n const defaultOptions = {\n component: 'tiny_ai',\n itemid: getEditorUtils(this.uniqid).getDraftItemId(),\n contextid: getEditorUtils(this.uniqid).getContextId()\n };\n if (['tts', 'audiogen'].includes(this.getCurrentTool())) {\n defaultOptions.filename = 'audio_' + Math.random().toString(16).slice(2) + '.mp3';\n } else if (this.getCurrentTool() === 'imggen') {\n defaultOptions.filename = 'img_' + Math.random().toString(16).slice(2) + '.png';\n }\n return defaultOptions;\n }\n\n setCurrentTool(currentTool) {\n this.currentTool = currentTool;\n }\n\n getCurrentTool() {\n return this.currentTool;\n }\n\n setCurrentPrompt(prompt) {\n this.prompt = prompt;\n const promptUpdatedEvent = new CustomEvent('promptUpdated', {\n detail: {\n newPrompt: prompt\n }\n });\n this.eventEmitterElement.dispatchEvent(promptUpdatedEvent);\n }\n\n getCurrentPrompt() {\n return this.prompt;\n\n }\n\n setCurrentFile(file) {\n this.file = file;\n }\n\n getCurrentFile() {\n return this.file;\n\n }\n\n getSelection() {\n return this.selection;\n }\n\n getSelectionText() {\n const span = document.createElement('span');\n span.innerHTML = this.selection;\n return span.textContent;\n }\n\n setSelection(selection) {\n this.selection = selection;\n }\n\n getSelectionImg() {\n return this.selectionImg;\n }\n\n setSelectionImg(image) {\n this.selectionImg = image;\n }\n\n getEventEmitterElement() {\n return this.eventEmitterElement;\n }\n\n setCurrentAiResult(aiResult) {\n this.currentAiResult = aiResult;\n }\n\n getCurrentAiResult() {\n return this.currentAiResult;\n }\n\n setCurrentOptions(options) {\n this.options = options;\n }\n\n getCurrentOptions() {\n const optionsToReturn = this.options === null ? {} : this.options;\n Object.assign(optionsToReturn, this.getDefaultOptions());\n return optionsToReturn;\n }\n\n reset() {\n this.setCurrentPrompt('');\n this.setCurrentOptions(null);\n this.setCurrentTool(null);\n this.setCurrentAiResult(null);\n this.setCurrentFile(null);\n }\n}\n"],"names":["constructor","uniqid","eventEmitterElement","document","createElement","getDefaultOptions","defaultOptions","component","itemid","this","getDraftItemId","contextid","getContextId","includes","getCurrentTool","filename","Math","random","toString","slice","setCurrentTool","currentTool","setCurrentPrompt","prompt","promptUpdatedEvent","CustomEvent","detail","newPrompt","dispatchEvent","getCurrentPrompt","setCurrentFile","file","getCurrentFile","getSelection","selection","getSelectionText","span","innerHTML","textContent","setSelection","getSelectionImg","selectionImg","setSelectionImg","image","getEventEmitterElement","setCurrentAiResult","aiResult","currentAiResult","getCurrentAiResult","setCurrentOptions","options","getCurrentOptions","optionsToReturn","Object","assign","reset"],"mappings":";;;;;;;;mHA8BIA,YAAYC,sCAFH,yCAOK,6CACI,oCACT,kCACF,qCACG,uCACE,0CACG,WAVNA,OAASA,YACTC,oBAAsBC,SAASC,cAAc,OAWtDC,0BACUC,eAAiB,CACnBC,UAAW,UACXC,QAAQ,yBAAeC,KAAKR,QAAQS,iBACpCC,WAAW,yBAAeF,KAAKR,QAAQW,sBAEvC,CAAC,MAAO,YAAYC,SAASJ,KAAKK,kBAClCR,eAAeS,SAAW,SAAWC,KAAKC,SAASC,SAAS,IAAIC,MAAM,GAAK,OAC1C,WAA1BV,KAAKK,mBACZR,eAAeS,SAAW,OAASC,KAAKC,SAASC,SAAS,IAAIC,MAAM,GAAK,QAEtEb,eAGXc,eAAeC,kBACNA,YAAcA,YAGvBP,wBACWL,KAAKY,YAGhBC,iBAAiBC,aACRA,OAASA,aACRC,mBAAqB,IAAIC,YAAY,gBAAiB,CACxDC,OAAQ,CACJC,UAAWJ,eAGdrB,oBAAoB0B,cAAcJ,oBAG3CK,0BACWpB,KAAKc,OAIhBO,eAAeC,WACNA,KAAOA,KAGhBC,wBACWvB,KAAKsB,KAIhBE,sBACWxB,KAAKyB,UAGhBC,yBACUC,KAAOjC,SAASC,cAAc,eACpCgC,KAAKC,UAAY5B,KAAKyB,UACfE,KAAKE,YAGhBC,aAAaL,gBACJA,UAAYA,UAGrBM,yBACW/B,KAAKgC,aAGhBC,gBAAgBC,YACPF,aAAeE,MAGxBC,gCACWnC,KAAKP,oBAGhB2C,mBAAmBC,eACVC,gBAAkBD,SAG3BE,4BACWvC,KAAKsC,gBAGhBE,kBAAkBC,cACTA,QAAUA,QAGnBC,0BACUC,gBAAmC,OAAjB3C,KAAKyC,QAAmB,GAAKzC,KAAKyC,eAC1DG,OAAOC,OAAOF,gBAAiB3C,KAAKJ,qBAC7B+C,gBAGXG,aACSjC,iBAAiB,SACjB2B,kBAAkB,WAClB7B,eAAe,WACfyB,mBAAmB,WACnBf,eAAe"}
\ No newline at end of file
diff --git a/amd/build/editor_utils.min.js b/amd/build/editor_utils.min.js
index 78e1bc4..5d07002 100644
--- a/amd/build/editor_utils.min.js
+++ b/amd/build/editor_utils.min.js
@@ -1,3 +1,3 @@
-define("tiny_ai/editor_utils",["exports","tiny_ai/modal","core/modal_events","tiny_ai/options","tiny_ai/constants","tiny_ai/common","editor_tiny/options","tiny_ai/utils"],(function(_exports,_modal,_modal_events,_options,_constants,_common,_options2,_utils){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_modal_events=_interopRequireDefault(_modal_events);return _exports.default=class{constructor(uniqid,editor){_defineProperty(this,"uniqid",null),_defineProperty(this,"userId",null),_defineProperty(this,"modal",null),_defineProperty(this,"mode",null),_defineProperty(this,"editor",null),this.uniqid=uniqid,this.editor=editor,this.userId=(0,_options.getUserId)(editor)}async displayDialogue(source){source===_common.selectionbarSource||this.editor.selection.getContent().length>0?this.mode=_constants.constants.modalModes.selection:source!==_common.toolbarSource&&source!==_common.menubarSource||(this.mode=_constants.constants.modalModes.general),this.modal=await _modal.default.create({templateContext:{classes:"tiny_ai-modal--dialog",headerclasses:"tiny_ai-modal--header"}}),this.modal.show(),this.mode===_constants.constants.modalModes.selection&&(0,_utils.getDatamanager)(this.uniqid).setSelection(this.editor.selection.getContent());const renderer=(0,_utils.getRenderer)(this.uniqid);await renderer.renderStart(),this.modal.getRoot().on(_modal_events.default.outsideClick,(event=>{event.preventDefault()}))}insertAfterContent(textToInsert){this.editor.setContent(this.editor.getContent()+"
"+textToInsert+"
")}replaceSelection(textReplacement){this.editor.selection.setContent(textReplacement)}getDraftItemId(){return(0,_options2.getDraftItemId)(this.editor)}getContextId(){return(0,_options2.getContextId)(this.editor)}getMode(){return this.mode}getModal(){return this.modal}getUserId(){return this.userId}},_exports.default}));
+define("tiny_ai/editor_utils",["exports","tiny_ai/modal","core/modal_events","tiny_ai/options","tiny_ai/constants","tiny_ai/common","editor_tiny/options","tiny_ai/utils","tiny_ai/datahandler/basedata"],(function(_exports,_modal,_modal_events,_options,_constants,_common,_options2,_utils,BasedataHandler){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_modal_events=_interopRequireDefault(_modal_events),BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler);return _exports.default=class{constructor(uniqid,editor){_defineProperty(this,"uniqid",null),_defineProperty(this,"userId",null),_defineProperty(this,"modal",null),_defineProperty(this,"mode",null),_defineProperty(this,"editor",null),this.uniqid=uniqid,this.editor=editor,this.userId=(0,_options.getUserId)(editor)}async displayDialogue(source){source===_common.selectionbarSource||this.editor.selection.getContent().length>0?this.mode=_constants.constants.modalModes.selection:source!==_common.toolbarSource&&source!==_common.menubarSource||(this.mode=_constants.constants.modalModes.general),this.modal=await _modal.default.create({templateContext:{classes:"tiny_ai-modal--dialog",headerclasses:"tiny_ai-modal--header"}}),this.modal.show();const renderer=(0,_utils.getRenderer)(this.uniqid);if((0,_utils.getDatamanager)(this.uniqid).setSelectionImg(null),this.mode===_constants.constants.modalModes.selection){const selectedEditorContentHtml=this.editor.selection.getContent({format:"html"}),images=(new DOMParser).parseFromString(selectedEditorContentHtml,"text/html").querySelectorAll("img");if(images.length>0&&images[0].src){const image=images[0],fetchResult=await fetch(image.src),data=await fetchResult.blob();(0,_utils.getDatamanager)(this.uniqid).setSelectionImg(data)}(0,_utils.getDatamanager)(this.uniqid).setSelection(this.editor.selection.getContent())}await renderer.renderStart(),this.modal.getRoot().on(_modal_events.default.outsideClick,(event=>{event.preventDefault()}))}insertAfterContent(textToInsert){this.editor.setContent(this.editor.getContent()+""+textToInsert+"
")}replaceSelection(textReplacement){this.editor.selection.setContent(textReplacement)}getDraftItemId(){return(0,_options2.getDraftItemId)(this.editor)}getContextId(){return(0,_options2.getContextId)(this.editor)}getMode(){return this.mode}getModal(){return this.modal}getUserId(){return this.userId}},_exports.default}));
//# sourceMappingURL=editor_utils.min.js.map
\ No newline at end of file
diff --git a/amd/build/editor_utils.min.js.map b/amd/build/editor_utils.min.js.map
index 3be25bc..aa21be3 100644
--- a/amd/build/editor_utils.min.js.map
+++ b/amd/build/editor_utils.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"editor_utils.min.js","sources":["../src/editor_utils.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 .\n\n/**\n * Editor instance specific utils.\n *\n * @module tiny_ai/editor_utils\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport AiModal from 'tiny_ai/modal';\nimport ModalEvents from 'core/modal_events';\nimport {getUserId} from 'tiny_ai/options';\nimport {constants} from 'tiny_ai/constants';\nimport {selectionbarSource, toolbarSource, menubarSource} from 'tiny_ai/common';\nimport {getDraftItemId as getDraftItemIdTinyCore, getContextId as getContextItemIdTinyCore} from 'editor_tiny/options';\nimport {getRenderer, getDatamanager} from 'tiny_ai/utils';\n\n\nexport default class {\n\n uniqid = null;\n userId = null;\n modal = null;\n mode = null;\n editor = null;\n\n constructor(uniqid, editor) {\n this.uniqid = uniqid;\n this.editor = editor;\n this.userId = getUserId(editor);\n }\n\n /**\n * Shows and handles the dialog.\n *\n * @param {string} source the different sources from where the modal is being created, defined in common module\n */\n async displayDialogue(source) {\n if (source === selectionbarSource || this.editor.selection.getContent().length > 0) {\n this.mode = constants.modalModes.selection;\n } else if (source === toolbarSource || source === menubarSource) {\n this.mode = constants.modalModes.general;\n }\n\n // We initially render the modal without content, because we need to rerender it anyway.\n this.modal = await AiModal.create({\n templateContext: {\n classes: 'tiny_ai-modal--dialog',\n headerclasses: 'tiny_ai-modal--header'\n }\n });\n this.modal.show();\n\n if (this.mode === constants.modalModes.selection) {\n getDatamanager(this.uniqid).setSelection(this.editor.selection.getContent());\n }\n const renderer = getRenderer(this.uniqid);\n // Unfortunately, the modal will not execute any JS code in the template, so we need to rerender the modal as a whole again.\n await renderer.renderStart();\n this.modal.getRoot().on(ModalEvents.outsideClick, event => {\n event.preventDefault();\n });\n }\n\n\n insertAfterContent(textToInsert) {\n this.editor.setContent(this.editor.getContent() + '' + textToInsert + '
');\n }\n\n /**\n * Replaces a selected text with the given replacement.\n *\n * In case nothing is selected, it will be inserted at the current caret position.\n *\n * @param {strings} textReplacement the text by which the current selection will be replaced or which will be inserted\n * at the caret (if no selection), can be HTML code\n */\n replaceSelection(textReplacement) {\n this.editor.selection.setContent(textReplacement);\n }\n\n getDraftItemId() {\n return getDraftItemIdTinyCore(this.editor);\n }\n\n getContextId() {\n return getContextItemIdTinyCore(this.editor);\n }\n\n getMode() {\n return this.mode;\n }\n\n getModal() {\n return this.modal;\n }\n\n getUserId() {\n return this.userId;\n }\n\n}\n"],"names":["constructor","uniqid","editor","userId","source","selectionbarSource","this","selection","getContent","length","mode","constants","modalModes","toolbarSource","menubarSource","general","modal","AiModal","create","templateContext","classes","headerclasses","show","setSelection","renderer","renderStart","getRoot","on","ModalEvents","outsideClick","event","preventDefault","insertAfterContent","textToInsert","setContent","replaceSelection","textReplacement","getDraftItemId","getContextId","getMode","getModal","getUserId"],"mappings":"+rBAyCIA,YAAYC,OAAQC,sCANX,oCACA,mCACD,kCACD,oCACE,WAGAD,OAASA,YACTC,OAASA,YACTC,QAAS,sBAAUD,8BAQNE,QACdA,SAAWC,4BAAsBC,KAAKJ,OAAOK,UAAUC,aAAaC,OAAS,OACxEC,KAAOC,qBAAUC,WAAWL,UAC1BH,SAAWS,uBAAiBT,SAAWU,6BACzCJ,KAAOC,qBAAUC,WAAWG,cAIhCC,YAAcC,eAAQC,OAAO,CAC9BC,gBAAiB,CACbC,QAAS,wBACTC,cAAe,gCAGlBL,MAAMM,OAEPhB,KAAKI,OAASC,qBAAUC,WAAWL,qCACpBD,KAAKL,QAAQsB,aAAajB,KAAKJ,OAAOK,UAAUC,oBAE7DgB,UAAW,sBAAYlB,KAAKL,cAE5BuB,SAASC,mBACVT,MAAMU,UAAUC,GAAGC,sBAAYC,cAAcC,QAC9CA,MAAMC,oBAKdC,mBAAmBC,mBACV/B,OAAOgC,WAAW5B,KAAKJ,OAAOM,aAAe,MAAQyB,aAAe,QAW7EE,iBAAiBC,sBACRlC,OAAOK,UAAU2B,WAAWE,iBAGrCC,wBACW,4BAAuB/B,KAAKJ,QAGvCoC,sBACW,0BAAyBhC,KAAKJ,QAGzCqC,iBACWjC,KAAKI,KAGhB8B,kBACWlC,KAAKU,MAGhByB,mBACWnC,KAAKH"}
\ No newline at end of file
+{"version":3,"file":"editor_utils.min.js","sources":["../src/editor_utils.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 .\n\n/**\n * Editor instance specific utils.\n *\n * @module tiny_ai/editor_utils\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport AiModal from 'tiny_ai/modal';\nimport ModalEvents from 'core/modal_events';\nimport {getUserId} from 'tiny_ai/options';\nimport {constants} from 'tiny_ai/constants';\nimport {selectionbarSource, toolbarSource, menubarSource} from 'tiny_ai/common';\nimport {getDraftItemId as getDraftItemIdTinyCore, getContextId as getContextItemIdTinyCore} from 'editor_tiny/options';\nimport {getRenderer, getDatamanager, errorAlert} from 'tiny_ai/utils';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\n\nexport default class {\n\n uniqid = null;\n userId = null;\n modal = null;\n mode = null;\n editor = null;\n\n constructor(uniqid, editor) {\n this.uniqid = uniqid;\n this.editor = editor;\n this.userId = getUserId(editor);\n }\n\n /**\n * Shows and handles the dialog.\n *\n * @param {string} source the different sources from where the modal is being created, defined in common module\n */\n async displayDialogue(source) {\n if (source === selectionbarSource || this.editor.selection.getContent().length > 0) {\n this.mode = constants.modalModes.selection;\n } else if (source === toolbarSource || source === menubarSource) {\n this.mode = constants.modalModes.general;\n }\n\n // We initially render the modal without content, because we need to rerender it anyway.\n this.modal = await AiModal.create({\n templateContext: {\n classes: 'tiny_ai-modal--dialog',\n headerclasses: 'tiny_ai-modal--header'\n }\n });\n this.modal.show();\n const renderer = getRenderer(this.uniqid);\n\n getDatamanager(this.uniqid).setSelectionImg(null);\n if (this.mode === constants.modalModes.selection) {\n const selectedEditorContentHtml = this.editor.selection.getContent({format: 'html'});\n const parser = new DOMParser();\n const editorDom = parser.parseFromString(selectedEditorContentHtml, 'text/html');\n const images = editorDom.querySelectorAll('img');\n\n if (images.length > 0 && images[0].src) {\n // If there are more than one we just use the first one.\n const image = images[0];\n // This should work for both external and data urls.\n const fetchResult = await fetch(image.src);\n const data = await fetchResult.blob();\n getDatamanager(this.uniqid).setSelectionImg(data);\n }\n getDatamanager(this.uniqid).setSelection(this.editor.selection.getContent());\n }\n // Unfortunately, the modal will not execute any JS code in the template, so we need to rerender the modal as a whole again.\n await renderer.renderStart();\n this.modal.getRoot().on(ModalEvents.outsideClick, event => {\n event.preventDefault();\n });\n }\n\n\n insertAfterContent(textToInsert) {\n this.editor.setContent(this.editor.getContent() + '' + textToInsert + '
');\n }\n\n /**\n * Replaces a selected text with the given replacement.\n *\n * In case nothing is selected, it will be inserted at the current caret position.\n *\n * @param {strings} textReplacement the text by which the current selection will be replaced or which will be inserted\n * at the caret (if no selection), can be HTML code\n */\n replaceSelection(textReplacement) {\n this.editor.selection.setContent(textReplacement);\n }\n\n getDraftItemId() {\n return getDraftItemIdTinyCore(this.editor);\n }\n\n getContextId() {\n return getContextItemIdTinyCore(this.editor);\n }\n\n getMode() {\n return this.mode;\n }\n\n getModal() {\n return this.modal;\n }\n\n getUserId() {\n return this.userId;\n }\n\n}\n"],"names":["constructor","uniqid","editor","userId","source","selectionbarSource","this","selection","getContent","length","mode","constants","modalModes","toolbarSource","menubarSource","general","modal","AiModal","create","templateContext","classes","headerclasses","show","renderer","setSelectionImg","selectedEditorContentHtml","format","images","DOMParser","parseFromString","querySelectorAll","src","image","fetchResult","fetch","data","blob","setSelection","renderStart","getRoot","on","ModalEvents","outsideClick","event","preventDefault","insertAfterContent","textToInsert","setContent","replaceSelection","textReplacement","getDraftItemId","getContextId","getMode","getModal","getUserId"],"mappings":"+qDAyCIA,YAAYC,OAAQC,sCANX,oCACA,mCACD,kCACD,oCACE,WAGAD,OAASA,YACTC,OAASA,YACTC,QAAS,sBAAUD,8BAQNE,QACdA,SAAWC,4BAAsBC,KAAKJ,OAAOK,UAAUC,aAAaC,OAAS,OACxEC,KAAOC,qBAAUC,WAAWL,UAC1BH,SAAWS,uBAAiBT,SAAWU,6BACzCJ,KAAOC,qBAAUC,WAAWG,cAIhCC,YAAcC,eAAQC,OAAO,CAC9BC,gBAAiB,CACbC,QAAS,wBACTC,cAAe,gCAGlBL,MAAMM,aACLC,UAAW,sBAAYjB,KAAKL,qCAEnBK,KAAKL,QAAQuB,gBAAgB,MACxClB,KAAKI,OAASC,qBAAUC,WAAWL,UAAW,OACxCkB,0BAA4BnB,KAAKJ,OAAOK,UAAUC,WAAW,CAACkB,OAAQ,SAGtEC,QAFS,IAAIC,WACMC,gBAAgBJ,0BAA2B,aAC3CK,iBAAiB,UAEtCH,OAAOlB,OAAS,GAAKkB,OAAO,GAAGI,IAAK,OAE9BC,MAAQL,OAAO,GAEfM,kBAAoBC,MAAMF,MAAMD,KAChCI,WAAaF,YAAYG,iCAChB9B,KAAKL,QAAQuB,gBAAgBW,gCAEjC7B,KAAKL,QAAQoC,aAAa/B,KAAKJ,OAAOK,UAAUC,oBAG7De,SAASe,mBACVtB,MAAMuB,UAAUC,GAAGC,sBAAYC,cAAcC,QAC9CA,MAAMC,oBAKdC,mBAAmBC,mBACV5C,OAAO6C,WAAWzC,KAAKJ,OAAOM,aAAe,MAAQsC,aAAe,QAW7EE,iBAAiBC,sBACR/C,OAAOK,UAAUwC,WAAWE,iBAGrCC,wBACW,4BAAuB5C,KAAKJ,QAGvCiD,sBACW,0BAAyB7C,KAAKJ,QAGzCkD,iBACW9C,KAAKI,KAGhB2C,kBACW/C,KAAKU,MAGhBsC,mBACWhD,KAAKH"}
\ No newline at end of file
diff --git a/amd/build/renderer.min.js b/amd/build/renderer.min.js
index 59118f4..b99d2ac 100644
--- a/amd/build/renderer.min.js
+++ b/amd/build/renderer.min.js
@@ -1,3 +1,3 @@
-define("tiny_ai/renderer",["exports","local_ai_manager/infobox","local_ai_manager/userquota","tiny_ai/datahandler/basedata","core/templates","jquery","tiny_ai/utils"],(function(_exports,_infobox,_userquota,BasedataHandler,_templates,_jquery,_utils){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler),_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery);return _exports.default=class{constructor(uniqid){_defineProperty(this,"uniqid",null),_defineProperty(this,"datamanager",null),_defineProperty(this,"editorUtils",null),this.uniqid=uniqid,this.datamanager=(0,_utils.getDatamanager)(uniqid),this.editorUtils=(0,_utils.getEditorUtils)(uniqid)}async renderStart(){this.datamanager.reset();const templateContext=await(0,_utils.getStartHandler)(this.uniqid).getTemplateContext((0,_utils.getEditorUtils)(this.uniqid));await this.renderModalContent("moodle-modal-body-start","moodle-modal-footer-info",templateContext)}async renderSummarize(){const templateContext=(0,_utils.getSummarizeHandler)(this.uniqid).getTemplateContext("summarize");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderTranslate(){const templateContext=(0,_utils.getTranslateHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderDescribe(){const templateContext=(0,_utils.getSummarizeHandler)(this.uniqid).getTemplateContext("describe");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderTts(){const templateContext=await(0,_utils.getTtsHandler)(this.uniqid).getTemplateContext("tts");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderAudiogen(){const templateContext=await(0,_utils.getTtsHandler)(this.uniqid).getTemplateContext("audiogen");await this.renderModalContent("moodle-modal-body-mediageneration","moodle-modal-footer-generate",templateContext)}async renderImggen(){const templateContext=await(0,_utils.getImggenHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-mediageneration","moodle-modal-footer-generate",templateContext)}async renderLoading(){const templateContext={};templateContext.modal_headline=BasedataHandler.getTinyAiString("aigenerating"),await this.renderModalContent("moodle-modal-body-loading","moodle-modal-footer-empty",templateContext)}async renderSuggestion(){const templateContext={};templateContext.modal_headline=BasedataHandler.getTinyAiString("aisuggestion"),templateContext.result_text=this.renderAiResultForEditor(),Object.assign(templateContext,BasedataHandler.getReplaceButtonsContext(this.editorUtils.getMode())),await this.renderModalContent("moodle-modal-body-suggestion","moodle-modal-footer-replace",templateContext)}async renderOptimizePrompt(){const templateContext=(0,_utils.getOptimizeHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-optimize","moodle-modal-footer-generate",templateContext)}async renderDismiss(){const templateContext={modal_headline:"",centered_headline:BasedataHandler.getTinyAiString("dismisssuggestion"),showIcon:!1,buttons:[{hasText:!0,button_text:BasedataHandler.getTinyAiString("cancel"),icon_left:!1,icon_right:!1,primary:!1,secondary:!0,tertiary:!1,action:"canceldismiss"},{hasText:!0,button_text:BasedataHandler.getTinyAiString("dismiss"),icon_left:!1,icon_right:!1,primary:!0,secondary:!1,tertiary:!1,action:"dismiss"}]};await this.renderModalContent("moodle-modal-body-dismiss","moodle-modal-footer-empty",templateContext)}renderAiResultForEditor(){let html;switch(this.datamanager.getCurrentTool()){case"tts":case"audiogen":{const audioPlayer=document.createElement("audio");audioPlayer.controls="controls",audioPlayer.src=this.datamanager.getCurrentAiResult(),audioPlayer.type="audio/mpeg",html=audioPlayer.outerHTML;break}case"imggen":{const img=document.createElement("img");img.src=this.datamanager.getCurrentAiResult(),img.classList.add("mw-100"),html=img.outerHTML;break}default:html=this.datamanager.getCurrentAiResult()}return html}async renderModalContent(bodyComponentTemplate,footerComponentTemplate,templateContext){templateContext.tinyinstanceuniqid=this.uniqid;const modal=(0,_utils.getEditorUtils)(this.uniqid).getModal();document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")}));const result=await Promise.all([_templates.default.renderForPromise("tiny_ai/components/moodle-modal-header-title",templateContext),_templates.default.renderForPromise("tiny_ai/components/"+bodyComponentTemplate,templateContext),_templates.default.renderForPromise("tiny_ai/components/"+footerComponentTemplate,templateContext)]);templateContext.hasOwnProperty("modal_headline")&&modal.setTitle(result[0].html),document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")})),modal.setBody(result[1].html),modal.setFooter(result[2].html),result.forEach((item=>{_templates.default.runTemplateJS(item.js)})),modal.getRoot().attr("data-tiny_ai_uniqid",this.uniqid),await this.insertInfoBox(),await this.insertUserQuotaBox(),document.querySelectorAll("button[data-action]").forEach((button=>{button.addEventListener("click",(event=>{(0,_jquery.default)(event.target).closest("button[data-action]").tooltip("hide")}))}))}async insertInfoBox(){document.querySelector('[data-rendertarget="infobox"]')&&await(0,_infobox.renderInfoBox)("tiny_ai",(0,_utils.getEditorUtils)(this.uniqid).getUserId(),'[data-rendertarget="infobox"]',["singleprompt","translate","tts","imggen"])}async insertUserQuotaBox(){document.querySelector('[data-rendertarget="usageinfo"]')&&await(0,_userquota.renderUserQuota)('[data-rendertarget="usageinfo"]',["singleprompt","translate","tts","imggen"])}},_exports.default}));
+define("tiny_ai/renderer",["exports","local_ai_manager/infobox","local_ai_manager/userquota","tiny_ai/datahandler/basedata","core/templates","jquery","tiny_ai/utils"],(function(_exports,_infobox,_userquota,BasedataHandler,_templates,_jquery,_utils){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler),_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery);return _exports.default=class{constructor(uniqid){_defineProperty(this,"uniqid",null),_defineProperty(this,"datamanager",null),_defineProperty(this,"editorUtils",null),this.uniqid=uniqid,this.datamanager=(0,_utils.getDatamanager)(uniqid),this.editorUtils=(0,_utils.getEditorUtils)(uniqid)}async renderStart(){this.datamanager.reset();const templateContext=await(0,_utils.getStartHandler)(this.uniqid).getTemplateContext((0,_utils.getEditorUtils)(this.uniqid));await this.renderModalContent("moodle-modal-body-start","moodle-modal-footer-info",templateContext)}async renderSummarize(){const templateContext=(0,_utils.getSummarizeHandler)(this.uniqid).getTemplateContext("summarize");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderTranslate(){const templateContext=(0,_utils.getTranslateHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderDescribe(){const templateContext=(0,_utils.getSummarizeHandler)(this.uniqid).getTemplateContext("describe");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderTts(){const templateContext=await(0,_utils.getTtsHandler)(this.uniqid).getTemplateContext("tts");await this.renderModalContent("moodle-modal-body-preferences","moodle-modal-footer-generate",templateContext)}async renderAudiogen(){const templateContext=await(0,_utils.getTtsHandler)(this.uniqid).getTemplateContext("audiogen");await this.renderModalContent("moodle-modal-body-mediageneration","moodle-modal-footer-generate",templateContext)}async renderImggen(){const templateContext=await(0,_utils.getImggenHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-mediageneration","moodle-modal-footer-generate",templateContext)}async renderDescribeimg(){const templateContext=await(0,_utils.getIttHandler)(this.uniqid).getTemplateContext("describeimg");await this.renderModalContent("moodle-modal-body-itt","moodle-modal-footer-generate",templateContext)}async renderImagetotext(){const templateContext=await(0,_utils.getIttHandler)(this.uniqid).getTemplateContext("imagetotext");await this.renderModalContent("moodle-modal-body-itt","moodle-modal-footer-generate",templateContext)}async renderLoading(){const templateContext={};templateContext.modal_headline=BasedataHandler.getTinyAiString("aigenerating"),await this.renderModalContent("moodle-modal-body-loading","moodle-modal-footer-empty",templateContext)}async renderSuggestion(){const templateContext={};templateContext.modal_headline=BasedataHandler.getTinyAiString("aisuggestion"),templateContext.result_text=this.renderAiResultForEditor(),Object.assign(templateContext,BasedataHandler.getReplaceButtonsContext(this.editorUtils.getMode())),await this.renderModalContent("moodle-modal-body-suggestion","moodle-modal-footer-replace",templateContext)}async renderOptimizePrompt(){const templateContext=(0,_utils.getOptimizeHandler)(this.uniqid).getTemplateContext();await this.renderModalContent("moodle-modal-body-optimize","moodle-modal-footer-generate",templateContext)}async renderDismiss(){const templateContext={modal_headline:"",centered_headline:BasedataHandler.getTinyAiString("dismisssuggestion"),showIcon:!1,buttons:[{hasText:!0,button_text:BasedataHandler.getTinyAiString("cancel"),icon_left:!1,icon_right:!1,primary:!1,secondary:!0,tertiary:!1,action:"canceldismiss"},{hasText:!0,button_text:BasedataHandler.getTinyAiString("dismiss"),icon_left:!1,icon_right:!1,primary:!0,secondary:!1,tertiary:!1,action:"dismiss"}]};await this.renderModalContent("moodle-modal-body-dismiss","moodle-modal-footer-empty",templateContext)}renderAiResultForEditor(){let html;switch(this.datamanager.getCurrentTool()){case"tts":case"audiogen":{const audioPlayer=document.createElement("audio");audioPlayer.controls="controls",audioPlayer.src=this.datamanager.getCurrentAiResult(),audioPlayer.type="audio/mpeg",html=audioPlayer.outerHTML;break}case"imggen":{const img=document.createElement("img");img.src=this.datamanager.getCurrentAiResult(),img.classList.add("mw-100"),html=img.outerHTML;break}default:html=this.datamanager.getCurrentAiResult()}return html}async renderModalContent(bodyComponentTemplate,footerComponentTemplate,templateContext){templateContext.tinyinstanceuniqid=this.uniqid;const modal=(0,_utils.getEditorUtils)(this.uniqid).getModal();document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")}));const result=await Promise.all([_templates.default.renderForPromise("tiny_ai/components/moodle-modal-header-title",templateContext),_templates.default.renderForPromise("tiny_ai/components/"+bodyComponentTemplate,templateContext),_templates.default.renderForPromise("tiny_ai/components/"+footerComponentTemplate,templateContext)]);templateContext.hasOwnProperty("modal_headline")&&modal.setTitle(result[0].html),document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")})),modal.setBody(result[1].html),modal.setFooter(result[2].html),result.forEach((item=>{_templates.default.runTemplateJS(item.js)})),modal.getRoot().attr("data-tiny_ai_uniqid",this.uniqid),await this.insertInfoBox(),await this.insertUserQuotaBox(),document.querySelectorAll("button[data-action]").forEach((button=>{button.addEventListener("click",(event=>{(0,_jquery.default)(event.target).closest("button[data-action]").tooltip("hide")}))}))}async insertInfoBox(){document.querySelector('[data-rendertarget="infobox"]')&&await(0,_infobox.renderInfoBox)("tiny_ai",(0,_utils.getEditorUtils)(this.uniqid).getUserId(),'[data-rendertarget="infobox"]',["singleprompt","translate","tts","imggen","itt"])}async insertUserQuotaBox(){document.querySelector('[data-rendertarget="usageinfo"]')&&await(0,_userquota.renderUserQuota)('[data-rendertarget="usageinfo"]',["singleprompt","translate","tts","imggen","itt"])}},_exports.default}));
//# sourceMappingURL=renderer.min.js.map
\ No newline at end of file
diff --git a/amd/build/renderer.min.js.map b/amd/build/renderer.min.js.map
index bc1fe65..12eda8a 100644
--- a/amd/build/renderer.min.js.map
+++ b/amd/build/renderer.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"renderer.min.js","sources":["../src/renderer.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 .\n\n/**\n * Tiny AI utils library.\n *\n * @module tiny_ai/renderer\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {renderInfoBox} from 'local_ai_manager/infobox';\nimport {renderUserQuota} from 'local_ai_manager/userquota';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport Templates from 'core/templates';\nimport $ from 'jquery';\nimport {\n getEditorUtils,\n getDatamanager,\n getImggenHandler,\n getOptimizeHandler,\n getStartHandler,\n getSummarizeHandler,\n getTranslateHandler,\n getTtsHandler\n} from 'tiny_ai/utils';\n\nexport default class {\n\n uniqid = null;\n datamanager = null;\n editorUtils = null;\n\n constructor(uniqid) {\n this.uniqid = uniqid;\n this.datamanager = getDatamanager(uniqid);\n this.editorUtils = getEditorUtils(uniqid);\n }\n\n async renderStart() {\n this.datamanager.reset();\n const templateContext = await getStartHandler(this.uniqid).getTemplateContext(getEditorUtils(this.uniqid));\n await this.renderModalContent('moodle-modal-body-start', 'moodle-modal-footer-info', templateContext);\n }\n\n\n async renderSummarize() {\n const templateContext = getSummarizeHandler(this.uniqid).getTemplateContext('summarize');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderTranslate() {\n const templateContext = getTranslateHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderDescribe() {\n const templateContext = getSummarizeHandler(this.uniqid).getTemplateContext('describe');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderTts() {\n const templateContext = await getTtsHandler(this.uniqid).getTemplateContext('tts');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderAudiogen() {\n const templateContext = await getTtsHandler(this.uniqid).getTemplateContext('audiogen');\n await this.renderModalContent('moodle-modal-body-mediageneration', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderImggen() {\n const templateContext = await getImggenHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-mediageneration', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderLoading() {\n const templateContext = {};\n templateContext.modal_headline = BasedataHandler.getTinyAiString('aigenerating');\n await this.renderModalContent('moodle-modal-body-loading', 'moodle-modal-footer-empty', templateContext);\n }\n\n\n async renderSuggestion() {\n const templateContext = {};\n templateContext.modal_headline = BasedataHandler.getTinyAiString('aisuggestion');\n // TODO Eventually do not use the same rendering in the suggestion like in the course, or just leave it because we\n // consider it beautiful\n templateContext.result_text = this.renderAiResultForEditor();\n\n Object.assign(templateContext, BasedataHandler.getReplaceButtonsContext(this.editorUtils.getMode()));\n await this.renderModalContent('moodle-modal-body-suggestion', 'moodle-modal-footer-replace', templateContext);\n }\n\n async renderOptimizePrompt() {\n const templateContext = getOptimizeHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-optimize', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderDismiss() {\n const templateContext = {\n modal_headline: '',\n centered_headline: BasedataHandler.getTinyAiString('dismisssuggestion'),\n showIcon: false,\n buttons: [\n {\n hasText: true,\n button_text: BasedataHandler.getTinyAiString('cancel'),\n icon_left: false,\n icon_right: false,\n primary: false,\n secondary: true,\n tertiary: false,\n action: 'canceldismiss'\n },\n {\n hasText: true,\n button_text: BasedataHandler.getTinyAiString('dismiss'),\n icon_left: false,\n icon_right: false,\n primary: true,\n secondary: false,\n tertiary: false,\n action: 'dismiss'\n }\n ]\n };\n await this.renderModalContent('moodle-modal-body-dismiss', 'moodle-modal-footer-empty', templateContext);\n }\n\n\n renderAiResultForEditor() {\n let html;\n switch (this.datamanager.getCurrentTool()) {\n case 'tts':\n case 'audiogen': {\n const audioPlayer = document.createElement('audio');\n audioPlayer.controls = 'controls';\n audioPlayer.src = this.datamanager.getCurrentAiResult();\n audioPlayer.type = 'audio/mpeg';\n html = audioPlayer.outerHTML;\n break;\n }\n case 'imggen': {\n const img = document.createElement('img');\n img.src = this.datamanager.getCurrentAiResult();\n img.classList.add('mw-100');\n html = img.outerHTML;\n break;\n }\n default: {\n html = this.datamanager.getCurrentAiResult();\n }\n }\n return html;\n }\n\n /**\n * Re-renders the content auf the modal once it has been created.\n *\n * @param {string} bodyComponentTemplate the name of the body template to use (without the prefix 'tiny_ai/components/')\n * @param {string} footerComponentTemplate the name of the footer template to use (without the prefix 'tiny_ai/components/')\n * @param {object} templateContext the template context being used for all partial templates\n * @returns {Promise} the async promise\n */\n async renderModalContent(bodyComponentTemplate, footerComponentTemplate, templateContext) {\n templateContext.tinyinstanceuniqid = this.uniqid;\n const modal = getEditorUtils(this.uniqid).getModal();\n // Remove all eventually remaining tooltips before rendering a new view.\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n const result = await Promise.all([\n Templates.renderForPromise('tiny_ai/components/moodle-modal-header-title', templateContext),\n Templates.renderForPromise('tiny_ai/components/' + bodyComponentTemplate, templateContext),\n Templates.renderForPromise('tiny_ai/components/' + footerComponentTemplate, templateContext)\n ]);\n if (templateContext.hasOwnProperty('modal_headline')) {\n // If there is no headline specified, we keep the old one.\n modal.setTitle(result[0].html);\n }\n // Hide all eventually still existing tooltips first, because they show on 'hover' and\n // 'focus'. So we need to remove them before removing the corresponding buttons from the DOM.\n // Boostrap 4 still using jQuery for tooltips, so we need jQuery here.\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n modal.setBody(result[1].html);\n modal.setFooter(result[2].html);\n result.forEach((item) => {\n Templates.runTemplateJS(item.js);\n });\n modal.getRoot().attr('data-tiny_ai_uniqid', this.uniqid);\n await this.insertInfoBox();\n await this.insertUserQuotaBox();\n document.querySelectorAll('button[data-action]').forEach(button => {\n button.addEventListener('click', event => {\n $(event.target).closest('button[data-action]').tooltip('hide');\n });\n });\n }\n\n async insertInfoBox() {\n const infoBoxSelector = '[data-rendertarget=\"infobox\"]';\n if (document.querySelector(infoBoxSelector)) {\n await renderInfoBox('tiny_ai', getEditorUtils(this.uniqid).getUserId(), infoBoxSelector,\n ['singleprompt', 'translate', 'tts', 'imggen']);\n }\n }\n\n async insertUserQuotaBox() {\n const usageBoxSelector = '[data-rendertarget=\"usageinfo\"]';\n if (document.querySelector(usageBoxSelector)) {\n await renderUserQuota(usageBoxSelector, ['singleprompt', 'translate', 'tts', 'imggen']);\n }\n }\n}\n"],"names":["constructor","uniqid","datamanager","editorUtils","reset","templateContext","this","getTemplateContext","renderModalContent","modal_headline","BasedataHandler","getTinyAiString","result_text","renderAiResultForEditor","Object","assign","getReplaceButtonsContext","getMode","centered_headline","showIcon","buttons","hasText","button_text","icon_left","icon_right","primary","secondary","tertiary","action","html","getCurrentTool","audioPlayer","document","createElement","controls","src","getCurrentAiResult","type","outerHTML","img","classList","add","bodyComponentTemplate","footerComponentTemplate","tinyinstanceuniqid","modal","getModal","querySelectorAll","forEach","button","tooltip","result","Promise","all","Templates","renderForPromise","hasOwnProperty","setTitle","setBody","setFooter","item","runTemplateJS","js","getRoot","attr","insertInfoBox","insertUserQuotaBox","addEventListener","event","target","closest","querySelector","getUserId"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"renderer.min.js","sources":["../src/renderer.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 .\n\n/**\n * Tiny AI utils library.\n *\n * @module tiny_ai/renderer\n * @copyright 2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {renderInfoBox} from 'local_ai_manager/infobox';\nimport {renderUserQuota} from 'local_ai_manager/userquota';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport Templates from 'core/templates';\nimport $ from 'jquery';\nimport {\n getEditorUtils,\n getDatamanager,\n getImggenHandler,\n getOptimizeHandler,\n getStartHandler,\n getSummarizeHandler,\n getTranslateHandler,\n getTtsHandler,\n getIttHandler\n} from 'tiny_ai/utils';\n\nexport default class {\n\n uniqid = null;\n datamanager = null;\n editorUtils = null;\n\n constructor(uniqid) {\n this.uniqid = uniqid;\n this.datamanager = getDatamanager(uniqid);\n this.editorUtils = getEditorUtils(uniqid);\n }\n\n async renderStart() {\n this.datamanager.reset();\n const templateContext = await getStartHandler(this.uniqid).getTemplateContext(getEditorUtils(this.uniqid));\n await this.renderModalContent('moodle-modal-body-start', 'moodle-modal-footer-info', templateContext);\n }\n\n\n async renderSummarize() {\n const templateContext = getSummarizeHandler(this.uniqid).getTemplateContext('summarize');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderTranslate() {\n const templateContext = getTranslateHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderDescribe() {\n const templateContext = getSummarizeHandler(this.uniqid).getTemplateContext('describe');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderTts() {\n const templateContext = await getTtsHandler(this.uniqid).getTemplateContext('tts');\n await this.renderModalContent('moodle-modal-body-preferences', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderAudiogen() {\n const templateContext = await getTtsHandler(this.uniqid).getTemplateContext('audiogen');\n await this.renderModalContent('moodle-modal-body-mediageneration', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderImggen() {\n const templateContext = await getImggenHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-mediageneration', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderDescribeimg() {\n const templateContext = await getIttHandler(this.uniqid).getTemplateContext('describeimg');\n await this.renderModalContent('moodle-modal-body-itt', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderImagetotext() {\n const templateContext = await getIttHandler(this.uniqid).getTemplateContext('imagetotext');\n await this.renderModalContent('moodle-modal-body-itt', 'moodle-modal-footer-generate', templateContext);\n }\n\n async renderLoading() {\n const templateContext = {};\n templateContext.modal_headline = BasedataHandler.getTinyAiString('aigenerating');\n await this.renderModalContent('moodle-modal-body-loading', 'moodle-modal-footer-empty', templateContext);\n }\n\n\n async renderSuggestion() {\n const templateContext = {};\n templateContext.modal_headline = BasedataHandler.getTinyAiString('aisuggestion');\n // TODO Eventually do not use the same rendering in the suggestion like in the course, or just leave it because we\n // consider it beautiful\n templateContext.result_text = this.renderAiResultForEditor();\n\n Object.assign(templateContext, BasedataHandler.getReplaceButtonsContext(this.editorUtils.getMode()));\n await this.renderModalContent('moodle-modal-body-suggestion', 'moodle-modal-footer-replace', templateContext);\n }\n\n async renderOptimizePrompt() {\n const templateContext = getOptimizeHandler(this.uniqid).getTemplateContext();\n await this.renderModalContent('moodle-modal-body-optimize', 'moodle-modal-footer-generate', templateContext);\n }\n\n\n async renderDismiss() {\n const templateContext = {\n modal_headline: '',\n centered_headline: BasedataHandler.getTinyAiString('dismisssuggestion'),\n showIcon: false,\n buttons: [\n {\n hasText: true,\n button_text: BasedataHandler.getTinyAiString('cancel'),\n icon_left: false,\n icon_right: false,\n primary: false,\n secondary: true,\n tertiary: false,\n action: 'canceldismiss'\n },\n {\n hasText: true,\n button_text: BasedataHandler.getTinyAiString('dismiss'),\n icon_left: false,\n icon_right: false,\n primary: true,\n secondary: false,\n tertiary: false,\n action: 'dismiss'\n }\n ]\n };\n await this.renderModalContent('moodle-modal-body-dismiss', 'moodle-modal-footer-empty', templateContext);\n }\n\n\n renderAiResultForEditor() {\n let html;\n switch (this.datamanager.getCurrentTool()) {\n case 'tts':\n case 'audiogen': {\n const audioPlayer = document.createElement('audio');\n audioPlayer.controls = 'controls';\n audioPlayer.src = this.datamanager.getCurrentAiResult();\n audioPlayer.type = 'audio/mpeg';\n html = audioPlayer.outerHTML;\n break;\n }\n case 'imggen': {\n const img = document.createElement('img');\n img.src = this.datamanager.getCurrentAiResult();\n img.classList.add('mw-100');\n html = img.outerHTML;\n break;\n }\n default: {\n html = this.datamanager.getCurrentAiResult();\n }\n }\n return html;\n }\n\n /**\n * Re-renders the content auf the modal once it has been created.\n *\n * @param {string} bodyComponentTemplate the name of the body template to use (without the prefix 'tiny_ai/components/')\n * @param {string} footerComponentTemplate the name of the footer template to use (without the prefix 'tiny_ai/components/')\n * @param {object} templateContext the template context being used for all partial templates\n * @returns {Promise} the async promise\n */\n async renderModalContent(bodyComponentTemplate, footerComponentTemplate, templateContext) {\n templateContext.tinyinstanceuniqid = this.uniqid;\n const modal = getEditorUtils(this.uniqid).getModal();\n // Remove all eventually remaining tooltips before rendering a new view.\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n const result = await Promise.all([\n Templates.renderForPromise('tiny_ai/components/moodle-modal-header-title', templateContext),\n Templates.renderForPromise('tiny_ai/components/' + bodyComponentTemplate, templateContext),\n Templates.renderForPromise('tiny_ai/components/' + footerComponentTemplate, templateContext)\n ]);\n if (templateContext.hasOwnProperty('modal_headline')) {\n // If there is no headline specified, we keep the old one.\n modal.setTitle(result[0].html);\n }\n // Hide all eventually still existing tooltips first, because they show on 'hover' and\n // 'focus'. So we need to remove them before removing the corresponding buttons from the DOM.\n // Boostrap 4 still using jQuery for tooltips, so we need jQuery here.\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n modal.setBody(result[1].html);\n modal.setFooter(result[2].html);\n result.forEach((item) => {\n Templates.runTemplateJS(item.js);\n });\n modal.getRoot().attr('data-tiny_ai_uniqid', this.uniqid);\n await this.insertInfoBox();\n await this.insertUserQuotaBox();\n document.querySelectorAll('button[data-action]').forEach(button => {\n button.addEventListener('click', event => {\n $(event.target).closest('button[data-action]').tooltip('hide');\n });\n });\n }\n\n async insertInfoBox() {\n const infoBoxSelector = '[data-rendertarget=\"infobox\"]';\n if (document.querySelector(infoBoxSelector)) {\n await renderInfoBox('tiny_ai', getEditorUtils(this.uniqid).getUserId(), infoBoxSelector,\n ['singleprompt', 'translate', 'tts', 'imggen', 'itt']);\n }\n }\n\n async insertUserQuotaBox() {\n const usageBoxSelector = '[data-rendertarget=\"usageinfo\"]';\n if (document.querySelector(usageBoxSelector)) {\n await renderUserQuota(usageBoxSelector, ['singleprompt', 'translate', 'tts', 'imggen', 'itt']);\n }\n }\n}\n"],"names":["constructor","uniqid","datamanager","editorUtils","reset","templateContext","this","getTemplateContext","renderModalContent","modal_headline","BasedataHandler","getTinyAiString","result_text","renderAiResultForEditor","Object","assign","getReplaceButtonsContext","getMode","centered_headline","showIcon","buttons","hasText","button_text","icon_left","icon_right","primary","secondary","tertiary","action","html","getCurrentTool","audioPlayer","document","createElement","controls","src","getCurrentAiResult","type","outerHTML","img","classList","add","bodyComponentTemplate","footerComponentTemplate","tinyinstanceuniqid","modal","getModal","querySelectorAll","forEach","button","tooltip","result","Promise","all","Templates","renderForPromise","hasOwnProperty","setTitle","setBody","setFooter","item","runTemplateJS","js","getRoot","attr","insertInfoBox","insertUserQuotaBox","addEventListener","event","target","closest","querySelector","getUserId"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/utils.min.js b/amd/build/utils.min.js
index 09913ff..893e7be 100644
--- a/amd/build/utils.min.js
+++ b/amd/build/utils.min.js
@@ -1,4 +1,4 @@
-define("tiny_ai/utils",["exports","core/modal_events","tiny_ai/renderer","tiny_ai/datamanager","tiny_ai/datahandler/imggen","tiny_ai/datahandler/optimize","tiny_ai/datahandler/start","tiny_ai/datahandler/summarize","tiny_ai/datahandler/translate","tiny_ai/datahandler/tts","core/notification","core/str","local_ai_manager/make_request","tiny_ai/datahandler/basedata","jquery","core/log","tiny_ai/editor_utils"],(function(_exports,_modal_events,_renderer,_datamanager,_imggen,_optimize,_start,_summarize,_translate,_tts,_notification,_str,_make_request,BasedataHandler,_jquery,_log,_editor_utils){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+define("tiny_ai/utils",["exports","core/modal_events","tiny_ai/renderer","tiny_ai/datamanager","tiny_ai/datahandler/imggen","tiny_ai/datahandler/optimize","tiny_ai/datahandler/start","tiny_ai/datahandler/summarize","tiny_ai/datahandler/translate","tiny_ai/datahandler/tts","tiny_ai/datahandler/itt","core/notification","core/str","local_ai_manager/make_request","tiny_ai/datahandler/basedata","jquery","core/log","tiny_ai/editor_utils"],(function(_exports,_modal_events,_renderer,_datamanager,_imggen,_optimize,_start,_summarize,_translate,_tts,_itt,_notification,_str,_make_request,BasedataHandler,_jquery,_log,_editor_utils){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Tiny AI utils library.
*
@@ -6,6 +6,6 @@ define("tiny_ai/utils",["exports","core/modal_events","tiny_ai/renderer","tiny_a
* @copyright 2024, ISB Bayern
* @author Dr. Peter Mayer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.stripHtmlTags=_exports.init=_exports.getTtsHandler=_exports.getTranslateHandler=_exports.getSummarizeHandler=_exports.getStartHandler=_exports.getRenderer=_exports.getOptimizeHandler=_exports.getImggenHandler=_exports.getEditorUtils=_exports.getDatamanager=_exports.getCurrentModalUniqId=_exports.getAiAnswer=_exports.errorAlert=void 0,_modal_events=_interopRequireDefault(_modal_events),_renderer=_interopRequireDefault(_renderer),_datamanager=_interopRequireDefault(_datamanager),_imggen=_interopRequireDefault(_imggen),_optimize=_interopRequireDefault(_optimize),_start=_interopRequireDefault(_start),_summarize=_interopRequireDefault(_summarize),_translate=_interopRequireDefault(_translate),_tts=_interopRequireDefault(_tts),BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler),_jquery=_interopRequireDefault(_jquery),_log=_interopRequireDefault(_log),_editor_utils=_interopRequireDefault(_editor_utils);const objectStore={};_exports.init=async(uniqid,editor)=>{objectStore.hasOwnProperty(uniqid)||(objectStore[uniqid]={},objectStore[uniqid].editorUtils=new _editor_utils.default(uniqid,editor),objectStore[uniqid].datamanager=new _datamanager.default(uniqid),await BasedataHandler.init(),objectStore[uniqid].imggenhandler=new _imggen.default(uniqid),objectStore[uniqid].optimizehandler=new _optimize.default(uniqid),objectStore[uniqid].starthandler=new _start.default(uniqid),await objectStore[uniqid].starthandler.init(),objectStore[uniqid].summarizehandler=new _summarize.default(uniqid),objectStore[uniqid].translatehandler=new _translate.default(uniqid),objectStore[uniqid].ttshandler=new _tts.default(uniqid),objectStore[uniqid].renderer=new _renderer.default(uniqid))};_exports.getAiAnswer=async function(prompt,purpose){let options=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},result=null;try{result=await(0,_make_request.makeRequest)(purpose,prompt,options)}catch(exception){return void await(0,_notification.exception)(exception)}if(200!==result.code){const alertTitle=await(0,_str.getString)("errorwithcode","tiny_ai",result.code),parsedResult=JSON.parse(result.result);return parsedResult.debuginfo&&_log.default.error(parsedResult.debuginfo),await errorAlert(parsedResult.message,alertTitle),null}return result.result};const errorAlert=async function(message){let title=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;null===title&&(title=BasedataHandler.getTinyAiString("generalerror"));const alertModal=await(0,_notification.alert)(title,message);alertModal.getRoot().on(_modal_events.default.hidden,(()=>{document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")}))}))};_exports.errorAlert=errorAlert;_exports.stripHtmlTags=textWithTags=>{const span=document.createElement("span");return span.innerHTML=textWithTags,span.textContent};_exports.getEditorUtils=uniqid=>objectStore[uniqid].editorUtils;_exports.getRenderer=uniqid=>objectStore[uniqid].renderer;_exports.getDatamanager=uniqid=>objectStore[uniqid].datamanager;_exports.getImggenHandler=uniqid=>objectStore[uniqid].imggenhandler;_exports.getOptimizeHandler=uniqid=>objectStore[uniqid].optimizehandler;_exports.getStartHandler=uniqid=>objectStore[uniqid].starthandler;_exports.getSummarizeHandler=uniqid=>objectStore[uniqid].summarizehandler;_exports.getTranslateHandler=uniqid=>objectStore[uniqid].translatehandler;_exports.getTtsHandler=uniqid=>objectStore[uniqid].ttshandler;_exports.getCurrentModalUniqId=element=>element.closest("[data-tiny_instance_uniqid]").dataset.tiny_instance_uniqid}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.stripHtmlTags=_exports.init=_exports.getTtsHandler=_exports.getTranslateHandler=_exports.getSummarizeHandler=_exports.getStartHandler=_exports.getRenderer=_exports.getOptimizeHandler=_exports.getIttHandler=_exports.getImggenHandler=_exports.getEditorUtils=_exports.getDatamanager=_exports.getCurrentModalUniqId=_exports.getAiAnswer=_exports.errorAlert=void 0,_modal_events=_interopRequireDefault(_modal_events),_renderer=_interopRequireDefault(_renderer),_datamanager=_interopRequireDefault(_datamanager),_imggen=_interopRequireDefault(_imggen),_optimize=_interopRequireDefault(_optimize),_start=_interopRequireDefault(_start),_summarize=_interopRequireDefault(_summarize),_translate=_interopRequireDefault(_translate),_tts=_interopRequireDefault(_tts),_itt=_interopRequireDefault(_itt),BasedataHandler=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(BasedataHandler),_jquery=_interopRequireDefault(_jquery),_log=_interopRequireDefault(_log),_editor_utils=_interopRequireDefault(_editor_utils);const objectStore={};_exports.init=async(uniqid,editor)=>{objectStore.hasOwnProperty(uniqid)||(objectStore[uniqid]={},objectStore[uniqid].editorUtils=new _editor_utils.default(uniqid,editor),objectStore[uniqid].datamanager=new _datamanager.default(uniqid),await BasedataHandler.init(),objectStore[uniqid].imggenhandler=new _imggen.default(uniqid),objectStore[uniqid].optimizehandler=new _optimize.default(uniqid),objectStore[uniqid].starthandler=new _start.default(uniqid),await objectStore[uniqid].starthandler.init(),objectStore[uniqid].summarizehandler=new _summarize.default(uniqid),objectStore[uniqid].translatehandler=new _translate.default(uniqid),objectStore[uniqid].ttshandler=new _tts.default(uniqid),objectStore[uniqid].itthandler=new _itt.default(uniqid),objectStore[uniqid].renderer=new _renderer.default(uniqid))};_exports.getAiAnswer=async function(prompt,purpose){let options=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},result=null;try{result=await(0,_make_request.makeRequest)(purpose,prompt,options)}catch(exception){return void await(0,_notification.exception)(exception)}if(200!==result.code){const alertTitle=await(0,_str.getString)("errorwithcode","tiny_ai",result.code),parsedResult=JSON.parse(result.result);return parsedResult.debuginfo&&_log.default.error(parsedResult.debuginfo),await errorAlert(parsedResult.message,alertTitle),null}return result.result};const errorAlert=async function(message){let title=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;null===title&&(title=BasedataHandler.getTinyAiString("generalerror"));const alertModal=await(0,_notification.alert)(title,message);alertModal.getRoot().on(_modal_events.default.hidden,(()=>{document.querySelectorAll("button[data-action]").forEach((button=>{(0,_jquery.default)(button).tooltip("hide")}))}))};_exports.errorAlert=errorAlert;_exports.stripHtmlTags=textWithTags=>{const span=document.createElement("span");return span.innerHTML=textWithTags,span.textContent};_exports.getEditorUtils=uniqid=>objectStore[uniqid].editorUtils;_exports.getRenderer=uniqid=>objectStore[uniqid].renderer;_exports.getDatamanager=uniqid=>objectStore[uniqid].datamanager;_exports.getImggenHandler=uniqid=>objectStore[uniqid].imggenhandler;_exports.getOptimizeHandler=uniqid=>objectStore[uniqid].optimizehandler;_exports.getStartHandler=uniqid=>objectStore[uniqid].starthandler;_exports.getSummarizeHandler=uniqid=>objectStore[uniqid].summarizehandler;_exports.getTranslateHandler=uniqid=>objectStore[uniqid].translatehandler;_exports.getTtsHandler=uniqid=>objectStore[uniqid].ttshandler;_exports.getIttHandler=uniqid=>objectStore[uniqid].itthandler;_exports.getCurrentModalUniqId=element=>element.closest("[data-tiny_instance_uniqid]").dataset.tiny_instance_uniqid}));
//# sourceMappingURL=utils.min.js.map
\ No newline at end of file
diff --git a/amd/build/utils.min.js.map b/amd/build/utils.min.js.map
index 850a124..8dbbfdc 100644
--- a/amd/build/utils.min.js.map
+++ b/amd/build/utils.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"utils.min.js","sources":["../src/utils.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 .\n\n/**\n * Tiny AI utils library.\n *\n * @module tiny_ai/utils\n * @copyright 2024, ISB Bayern\n * @author Dr. Peter Mayer\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalEvents from 'core/modal_events';\nimport Renderer from 'tiny_ai/renderer';\nimport DataManager from 'tiny_ai/datamanager';\nimport ImggenHandler from 'tiny_ai/datahandler/imggen';\nimport OptimizeHandler from 'tiny_ai/datahandler/optimize';\nimport StartHandler from 'tiny_ai/datahandler/start';\nimport SummarizeHandler from 'tiny_ai/datahandler/summarize';\nimport TranslateHandler from 'tiny_ai/datahandler/translate';\nimport TtsHandler from 'tiny_ai/datahandler/tts';\nimport {alert as Alert, exception as displayException} from 'core/notification';\nimport {getString} from 'core/str';\nimport {makeRequest} from 'local_ai_manager/make_request';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport $ from 'jquery';\nimport Log from 'core/log';\nimport EditorUtils from 'tiny_ai/editor_utils';\n\nconst objectStore = {};\n\nexport const init = async (uniqid, editor) => {\n if (!objectStore.hasOwnProperty(uniqid)) {\n objectStore[uniqid] = {};\n // The order in which these objects are being created is actually pretty important, because Renderer\n // object depends on DataManager object.\n objectStore[uniqid].editorUtils = new EditorUtils(uniqid, editor);\n objectStore[uniqid].datamanager = new DataManager(uniqid);\n await BasedataHandler.init();\n objectStore[uniqid].imggenhandler = new ImggenHandler(uniqid);\n objectStore[uniqid].optimizehandler = new OptimizeHandler(uniqid);\n objectStore[uniqid].starthandler = new StartHandler(uniqid);\n await objectStore[uniqid].starthandler.init();\n objectStore[uniqid].summarizehandler = new SummarizeHandler(uniqid);\n objectStore[uniqid].translatehandler = new TranslateHandler(uniqid);\n objectStore[uniqid].ttshandler = new TtsHandler(uniqid);\n objectStore[uniqid].renderer = new Renderer(uniqid);\n }\n};\n\nexport const getAiAnswer = async (prompt, purpose, options = {}) => {\n let result = null;\n try {\n result = await makeRequest(purpose, prompt, options);\n } catch (exception) {\n await displayException(exception);\n return;\n }\n if (result.code !== 200) {\n const alertTitle = await getString('errorwithcode', 'tiny_ai', result.code);\n const parsedResult = JSON.parse(result.result);\n if (parsedResult.debuginfo) {\n Log.error(parsedResult.debuginfo);\n }\n await errorAlert(parsedResult.message, alertTitle);\n return null;\n }\n return result.result;\n};\n\nexport const errorAlert = async (message, title = null) => {\n if (title === null) {\n title = BasedataHandler.getTinyAiString('generalerror');\n }\n const alertModal = await Alert(title, message);\n alertModal.getRoot().on(ModalEvents.hidden, () => {\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n });\n};\n\nexport const stripHtmlTags = (textWithTags) => {\n // Place selected content into a temporary span and extract the plain text from it to strip HTML tags.\n const span = document.createElement('span');\n span.innerHTML = textWithTags;\n return span.textContent;\n};\n\nexport const getEditorUtils = (uniqid) => {\n return objectStore[uniqid].editorUtils;\n};\n\nexport const getRenderer = (uniqid) => {\n return objectStore[uniqid].renderer;\n};\n\nexport const getDatamanager = (uniqid) => {\n return objectStore[uniqid].datamanager;\n};\n\nexport const getImggenHandler = (uniqid) => {\n return objectStore[uniqid].imggenhandler;\n};\n\nexport const getOptimizeHandler = (uniqid) => {\n return objectStore[uniqid].optimizehandler;\n};\n\nexport const getStartHandler = (uniqid) => {\n return objectStore[uniqid].starthandler;\n};\n\nexport const getSummarizeHandler = (uniqid) => {\n return objectStore[uniqid].summarizehandler;\n};\n\nexport const getTranslateHandler = (uniqid) => {\n return objectStore[uniqid].translatehandler;\n};\n\nexport const getTtsHandler = (uniqid) => {\n return objectStore[uniqid].ttshandler;\n};\n\nexport const getCurrentModalUniqId = (element) => {\n return element.closest('[data-tiny_instance_uniqid]').dataset.tiny_instance_uniqid;\n};\n"],"names":["objectStore","async","uniqid","editor","hasOwnProperty","editorUtils","EditorUtils","datamanager","DataManager","BasedataHandler","init","imggenhandler","ImggenHandler","optimizehandler","OptimizeHandler","starthandler","StartHandler","summarizehandler","SummarizeHandler","translatehandler","TranslateHandler","ttshandler","TtsHandler","renderer","Renderer","prompt","purpose","options","result","exception","code","alertTitle","parsedResult","JSON","parse","debuginfo","error","errorAlert","message","title","getTinyAiString","alertModal","getRoot","on","ModalEvents","hidden","document","querySelectorAll","forEach","button","tooltip","textWithTags","span","createElement","innerHTML","textContent","element","closest","dataset","tiny_instance_uniqid"],"mappings":";;;;;;;;klDAyCMA,YAAc,iBAEAC,MAAOC,OAAQC,UAC1BH,YAAYI,eAAeF,UAC5BF,YAAYE,QAAU,GAGtBF,YAAYE,QAAQG,YAAc,IAAIC,sBAAYJ,OAAQC,QAC1DH,YAAYE,QAAQK,YAAc,IAAIC,qBAAYN,cAC5CO,gBAAgBC,OACtBV,YAAYE,QAAQS,cAAgB,IAAIC,gBAAcV,QACtDF,YAAYE,QAAQW,gBAAkB,IAAIC,kBAAgBZ,QAC1DF,YAAYE,QAAQa,aAAe,IAAIC,eAAad,cAC9CF,YAAYE,QAAQa,aAAaL,OACvCV,YAAYE,QAAQe,iBAAmB,IAAIC,mBAAiBhB,QAC5DF,YAAYE,QAAQiB,iBAAmB,IAAIC,mBAAiBlB,QAC5DF,YAAYE,QAAQmB,WAAa,IAAIC,aAAWpB,QAChDF,YAAYE,QAAQqB,SAAW,IAAIC,kBAAStB,+BAIzBD,eAAOwB,OAAQC,aAASC,+DAAU,GACrDC,OAAS,SAETA,aAAe,6BAAYF,QAASD,OAAQE,SAC9C,MAAOE,6BACC,2BAAiBA,cAGP,MAAhBD,OAAOE,KAAc,OACfC,iBAAmB,kBAAU,gBAAiB,UAAWH,OAAOE,MAChEE,aAAeC,KAAKC,MAAMN,OAAOA,eACnCI,aAAaG,wBACTC,MAAMJ,aAAaG,iBAErBE,WAAWL,aAAaM,QAASP,YAChC,YAEJH,OAAOA,cAGLS,WAAapC,eAAOqC,aAASC,6DAAQ,KAChC,OAAVA,QACAA,MAAQ9B,gBAAgB+B,gBAAgB,uBAEtCC,iBAAmB,uBAAMF,MAAOD,SACtCG,WAAWC,UAAUC,GAAGC,sBAAYC,QAAQ,KACxCC,SAASC,iBAAiB,uBAAuBC,SAAQC,6BACnDA,QAAQC,QAAQ,qEAKAC,qBAEpBC,KAAON,SAASO,cAAc,eACpCD,KAAKE,UAAYH,aACVC,KAAKG,qCAGerD,QACpBF,YAAYE,QAAQG,iCAGHH,QACjBF,YAAYE,QAAQqB,iCAGArB,QACpBF,YAAYE,QAAQK,sCAGEL,QACtBF,YAAYE,QAAQS,0CAGIT,QACxBF,YAAYE,QAAQW,yCAGCX,QACrBF,YAAYE,QAAQa,0CAGKb,QACzBF,YAAYE,QAAQe,8CAGKf,QACzBF,YAAYE,QAAQiB,wCAGDjB,QACnBF,YAAYE,QAAQmB,0CAGOmC,SAC3BA,QAAQC,QAAQ,+BAA+BC,QAAQC"}
\ No newline at end of file
+{"version":3,"file":"utils.min.js","sources":["../src/utils.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 .\n\n/**\n * Tiny AI utils library.\n *\n * @module tiny_ai/utils\n * @copyright 2024, ISB Bayern\n * @author Dr. Peter Mayer\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalEvents from 'core/modal_events';\nimport Renderer from 'tiny_ai/renderer';\nimport DataManager from 'tiny_ai/datamanager';\nimport ImggenHandler from 'tiny_ai/datahandler/imggen';\nimport OptimizeHandler from 'tiny_ai/datahandler/optimize';\nimport StartHandler from 'tiny_ai/datahandler/start';\nimport SummarizeHandler from 'tiny_ai/datahandler/summarize';\nimport TranslateHandler from 'tiny_ai/datahandler/translate';\nimport TtsHandler from 'tiny_ai/datahandler/tts';\nimport IttHandler from 'tiny_ai/datahandler/itt';\nimport {alert as Alert, exception as displayException} from 'core/notification';\nimport {getString} from 'core/str';\nimport {makeRequest} from 'local_ai_manager/make_request';\nimport * as BasedataHandler from 'tiny_ai/datahandler/basedata';\nimport $ from 'jquery';\nimport Log from 'core/log';\nimport EditorUtils from 'tiny_ai/editor_utils';\n\nconst objectStore = {};\n\nexport const init = async (uniqid, editor) => {\n if (!objectStore.hasOwnProperty(uniqid)) {\n objectStore[uniqid] = {};\n // The order in which these objects are being created is actually pretty important, because Renderer\n // object depends on DataManager object.\n objectStore[uniqid].editorUtils = new EditorUtils(uniqid, editor);\n objectStore[uniqid].datamanager = new DataManager(uniqid);\n await BasedataHandler.init();\n objectStore[uniqid].imggenhandler = new ImggenHandler(uniqid);\n objectStore[uniqid].optimizehandler = new OptimizeHandler(uniqid);\n objectStore[uniqid].starthandler = new StartHandler(uniqid);\n await objectStore[uniqid].starthandler.init();\n objectStore[uniqid].summarizehandler = new SummarizeHandler(uniqid);\n objectStore[uniqid].translatehandler = new TranslateHandler(uniqid);\n objectStore[uniqid].ttshandler = new TtsHandler(uniqid);\n objectStore[uniqid].itthandler = new IttHandler(uniqid);\n objectStore[uniqid].renderer = new Renderer(uniqid);\n }\n};\n\nexport const getAiAnswer = async (prompt, purpose, options = {}) => {\n let result = null;\n try {\n result = await makeRequest(purpose, prompt, options);\n } catch (exception) {\n await displayException(exception);\n return;\n }\n if (result.code !== 200) {\n const alertTitle = await getString('errorwithcode', 'tiny_ai', result.code);\n const parsedResult = JSON.parse(result.result);\n if (parsedResult.debuginfo) {\n Log.error(parsedResult.debuginfo);\n }\n await errorAlert(parsedResult.message, alertTitle);\n return null;\n }\n return result.result;\n};\n\nexport const errorAlert = async (message, title = null) => {\n if (title === null) {\n title = BasedataHandler.getTinyAiString('generalerror');\n }\n const alertModal = await Alert(title, message);\n alertModal.getRoot().on(ModalEvents.hidden, () => {\n document.querySelectorAll('button[data-action]').forEach(button => {\n $(button).tooltip('hide');\n });\n });\n};\n\nexport const stripHtmlTags = (textWithTags) => {\n // Place selected content into a temporary span and extract the plain text from it to strip HTML tags.\n const span = document.createElement('span');\n span.innerHTML = textWithTags;\n return span.textContent;\n};\n\nexport const getEditorUtils = (uniqid) => {\n return objectStore[uniqid].editorUtils;\n};\n\nexport const getRenderer = (uniqid) => {\n return objectStore[uniqid].renderer;\n};\n\nexport const getDatamanager = (uniqid) => {\n return objectStore[uniqid].datamanager;\n};\n\nexport const getImggenHandler = (uniqid) => {\n return objectStore[uniqid].imggenhandler;\n};\n\nexport const getOptimizeHandler = (uniqid) => {\n return objectStore[uniqid].optimizehandler;\n};\n\nexport const getStartHandler = (uniqid) => {\n return objectStore[uniqid].starthandler;\n};\n\nexport const getSummarizeHandler = (uniqid) => {\n return objectStore[uniqid].summarizehandler;\n};\n\nexport const getTranslateHandler = (uniqid) => {\n return objectStore[uniqid].translatehandler;\n};\n\nexport const getTtsHandler = (uniqid) => {\n return objectStore[uniqid].ttshandler;\n};\n\nexport const getIttHandler = (uniqid) => {\n return objectStore[uniqid].itthandler;\n};\n\nexport const getCurrentModalUniqId = (element) => {\n return element.closest('[data-tiny_instance_uniqid]').dataset.tiny_instance_uniqid;\n};\n"],"names":["objectStore","async","uniqid","editor","hasOwnProperty","editorUtils","EditorUtils","datamanager","DataManager","BasedataHandler","init","imggenhandler","ImggenHandler","optimizehandler","OptimizeHandler","starthandler","StartHandler","summarizehandler","SummarizeHandler","translatehandler","TranslateHandler","ttshandler","TtsHandler","itthandler","IttHandler","renderer","Renderer","prompt","purpose","options","result","exception","code","alertTitle","parsedResult","JSON","parse","debuginfo","error","errorAlert","message","title","getTinyAiString","alertModal","getRoot","on","ModalEvents","hidden","document","querySelectorAll","forEach","button","tooltip","textWithTags","span","createElement","innerHTML","textContent","element","closest","dataset","tiny_instance_uniqid"],"mappings":";;;;;;;;2oDA0CMA,YAAc,iBAEAC,MAAOC,OAAQC,UAC1BH,YAAYI,eAAeF,UAC5BF,YAAYE,QAAU,GAGtBF,YAAYE,QAAQG,YAAc,IAAIC,sBAAYJ,OAAQC,QAC1DH,YAAYE,QAAQK,YAAc,IAAIC,qBAAYN,cAC5CO,gBAAgBC,OACtBV,YAAYE,QAAQS,cAAgB,IAAIC,gBAAcV,QACtDF,YAAYE,QAAQW,gBAAkB,IAAIC,kBAAgBZ,QAC1DF,YAAYE,QAAQa,aAAe,IAAIC,eAAad,cAC9CF,YAAYE,QAAQa,aAAaL,OACvCV,YAAYE,QAAQe,iBAAmB,IAAIC,mBAAiBhB,QAC5DF,YAAYE,QAAQiB,iBAAmB,IAAIC,mBAAiBlB,QAC5DF,YAAYE,QAAQmB,WAAa,IAAIC,aAAWpB,QAChDF,YAAYE,QAAQqB,WAAa,IAAIC,aAAWtB,QAChDF,YAAYE,QAAQuB,SAAW,IAAIC,kBAASxB,+BAIzBD,eAAO0B,OAAQC,aAASC,+DAAU,GACrDC,OAAS,SAETA,aAAe,6BAAYF,QAASD,OAAQE,SAC9C,MAAOE,6BACC,2BAAiBA,cAGP,MAAhBD,OAAOE,KAAc,OACfC,iBAAmB,kBAAU,gBAAiB,UAAWH,OAAOE,MAChEE,aAAeC,KAAKC,MAAMN,OAAOA,eACnCI,aAAaG,wBACTC,MAAMJ,aAAaG,iBAErBE,WAAWL,aAAaM,QAASP,YAChC,YAEJH,OAAOA,cAGLS,WAAatC,eAAOuC,aAASC,6DAAQ,KAChC,OAAVA,QACAA,MAAQhC,gBAAgBiC,gBAAgB,uBAEtCC,iBAAmB,uBAAMF,MAAOD,SACtCG,WAAWC,UAAUC,GAAGC,sBAAYC,QAAQ,KACxCC,SAASC,iBAAiB,uBAAuBC,SAAQC,6BACnDA,QAAQC,QAAQ,qEAKAC,qBAEpBC,KAAON,SAASO,cAAc,eACpCD,KAAKE,UAAYH,aACVC,KAAKG,qCAGevD,QACpBF,YAAYE,QAAQG,iCAGHH,QACjBF,YAAYE,QAAQuB,iCAGAvB,QACpBF,YAAYE,QAAQK,sCAGEL,QACtBF,YAAYE,QAAQS,0CAGIT,QACxBF,YAAYE,QAAQW,yCAGCX,QACrBF,YAAYE,QAAQa,0CAGKb,QACzBF,YAAYE,QAAQe,8CAGKf,QACzBF,YAAYE,QAAQiB,wCAGDjB,QACnBF,YAAYE,QAAQmB,kCAGDnB,QACnBF,YAAYE,QAAQqB,0CAGOmC,SAC3BA,QAAQC,QAAQ,+BAA+BC,QAAQC"}
\ No newline at end of file
diff --git a/amd/src/constants.js b/amd/src/constants.js
index 1ac6734..8d91b63 100644
--- a/amd/src/constants.js
+++ b/amd/src/constants.js
@@ -36,5 +36,7 @@ export const constants = {
imggen: 'imggen',
tts: 'tts',
freeprompt: 'singleprompt',
+ describeimg: 'itt',
+ imagetotext: 'itt'
}
};
diff --git a/amd/src/controllers/base.js b/amd/src/controllers/base.js
index 4dc2ab9..eaa6898 100644
--- a/amd/src/controllers/base.js
+++ b/amd/src/controllers/base.js
@@ -56,6 +56,11 @@ export default class {
await errorAlert(BasedataHandler.getTinyAiString('error_nopromptgiven'));
return null;
}
+ if (['describeimg', 'imagetotext'].includes(this.datamanager.getCurrentTool())
+ && this.datamanager.getCurrentFile() === null) {
+ await errorAlert(BasedataHandler.getTinyAiString('error_nofile'));
+ return null;
+ }
await this.renderer.renderLoading();
let result = null;
try {
diff --git a/amd/src/controllers/file.js b/amd/src/controllers/file.js
new file mode 100644
index 0000000..d981a5e
--- /dev/null
+++ b/amd/src/controllers/file.js
@@ -0,0 +1,151 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * Controller for handling the show/hide prompt button and the associated textarea.
+ *
+ * @module tiny_ai/controllers/file
+ * @copyright 2024, ISB Bayern
+ * @author Philipp Memmel
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {getDatamanager, getCurrentModalUniqId, getIttHandler} from 'tiny_ai/utils';
+import Templates from 'core/templates';
+import SELECTORS from 'tiny_ai/selectors';
+import {errorAlert} from 'tiny_ai/utils';
+import * as BasedataHandler from 'tiny_ai/datahandler/basedata';
+import {getString} from 'core/str';
+
+
+export default class {
+
+ dropzone = null;
+ dropzoneContentToResetTo = '';
+
+ constructor(baseSelector) {
+ this.baseElement = document.querySelector(baseSelector);
+ }
+
+ async init() {
+ this.dropzone = this.baseElement.querySelector('[data-type="dropzone"]');
+ const dropzone = this.dropzone;
+ // Setting contentEditable to true makes the browser show a "paste" option in the context menu when
+ // right-clicking the drop zone.
+ dropzone.contentEditable = true;
+ this.setDropzoneContent(dropzone.innerHTML);
+
+ const _this = this;
+ // The drop zone has "contentEditable" enabled, so we have to take care of user input
+ // and reset the content whenever a user tries to input something.
+ dropzone.addEventListener('input', () => {
+ if (dropzone.innerHTML !== _this.dropzoneContentToResetTo) {
+ dropzone.innerHTML = _this.dropzoneContentToResetTo;
+ }
+ });
+ dropzone.addEventListener('drop', async(event) => {
+ event.preventDefault();
+
+ if (event.dataTransfer.items) {
+ // Use DataTransferItemList interface to access the file(s)
+ const item = [...event.dataTransfer.items].shift();
+ // If dropped item is no file, reject it.
+ if (item.kind === 'file') {
+ await this.handleFile(item.getAsFile());
+ }
+ } else {
+ // Use DataTransfer interface to access the file(s)
+ await this.handleFile([...event.dataTransfer.files].shift());
+ }
+ });
+ document.querySelector(SELECTORS.modalDialog).addEventListener('paste', async(event) => {
+ event.preventDefault();
+ const clipboardData = (event.clipboardData || window.clipboardData);
+ if (clipboardData.files.length === 0) {
+ await errorAlert(BasedataHandler.getTinyAiString('error_nofileinclipboard_text'),
+ BasedataHandler.getTinyAiString('error_nofileinclipboard_title'));
+ return;
+ }
+ const file = clipboardData.files[0];
+ this.handleFile(file);
+ });
+ dropzone.addEventListener('dragover', (event) => {
+ event.preventDefault();
+ dropzone.classList.remove('tiny_ai_dropzone_filled');
+ dropzone.classList.add('tiny_ai_dragover');
+ });
+ dropzone.addEventListener('dragleave', (event) => {
+ event.preventDefault();
+ dropzone.classList.remove('tiny_ai_dragover');
+ });
+
+ const datamanager = getDatamanager(getCurrentModalUniqId(this.baseElement));
+ if (datamanager.getSelectionImg() !== null) {
+ await this.handleFile(datamanager.getSelectionImg());
+ }
+ }
+
+ async handleFile(file) {
+ const reader = new FileReader();
+ const _this = this;
+ reader.addEventListener(
+ 'load',
+ async() => {
+ const currentModalUniqid = getCurrentModalUniqId(this.baseElement);
+ const datamanager = getDatamanager(currentModalUniqid);
+ const fileUploadedEvent = new CustomEvent('fileUploaded', {
+ detail: {
+ newFile: reader.result,
+ }
+ });
+ datamanager.getEventEmitterElement().dispatchEvent(fileUploadedEvent);
+ const ittHandler = getIttHandler(currentModalUniqid);
+ const allowedMimetypes = await ittHandler.getAllowedMimetypes();
+
+ if (!allowedMimetypes.includes(file.type)) {
+ const errorTitle = await getString('error_unsupportedfiletype_title', 'tiny_ai');
+ const errorText = await getString('error_unsupportedfiletype_text', 'tiny_ai', allowedMimetypes.toString());
+ await errorAlert(errorText, errorTitle);
+ return;
+ }
+
+ const fileEntryTemplateContext = {
+ icon: file.type === 'application/pdf' ? 'fa-file-pdf' : 'fa-image',
+ filename: file.name ? file.name : BasedataHandler.getTinyAiString('imagefromeditor'),
+ };
+ if (file.type.startsWith('image')) {
+ fileEntryTemplateContext.isImage = true;
+ fileEntryTemplateContext.dataurl = reader.result;
+ }
+ const {html, js} = await Templates.renderForPromise('tiny_ai/components/ai-file-list-entry',
+ fileEntryTemplateContext);
+ _this.setDropzoneContent(html);
+ // We probably have no JS, but let's be safe here.
+ Templates.runTemplateJS(js);
+ // There should be no tiny_ai_dragover class, just to be safe.
+ _this.dropzone.classList.remove('tiny_ai_dragover');
+ _this.dropzone.classList.add('tiny_ai_dropzone_filled');
+ },
+ false,
+ );
+ reader.readAsDataURL(file);
+ }
+
+ setDropzoneContent(html) {
+ this.dropzone.innerHTML = html;
+ // Keep track of the state.
+ this.dropzoneContentToResetTo = html;
+ }
+}
diff --git a/amd/src/controllers/preferences.js b/amd/src/controllers/preferences.js
index c96f642..9a4569b 100644
--- a/amd/src/controllers/preferences.js
+++ b/amd/src/controllers/preferences.js
@@ -16,7 +16,7 @@
/**
* Controller for the main selection.
*
- * @module tiny_ai/controllers/translate
+ * @module tiny_ai/controllers/preferences
* @copyright 2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -25,7 +25,7 @@
import {constants} from 'tiny_ai/constants';
import SELECTORS from 'tiny_ai/selectors';
import BaseController from 'tiny_ai/controllers/base';
-import {getSummarizeHandler, getTranslateHandler, getTtsHandler, getImggenHandler} from 'tiny_ai/utils';
+import {getSummarizeHandler, getTranslateHandler, getTtsHandler, getImggenHandler, getIttHandler} from 'tiny_ai/utils';
export default class extends BaseController {
@@ -34,11 +34,12 @@ export default class extends BaseController {
const backButton = modalFooter.querySelector('[data-action="back"]');
const generateButton = modalFooter.querySelector('[data-action="generate"]');
- const [summarizeHandler, translateHandler, ttsHandler, imggenHandler] = [
+ const [summarizeHandler, translateHandler, ttsHandler, imggenHandler, ittHandler] = [
getSummarizeHandler(this.uniqid),
getTranslateHandler(this.uniqid),
getTtsHandler(this.uniqid),
- getImggenHandler(this.uniqid)
+ getImggenHandler(this.uniqid),
+ getIttHandler(this.uniqid)
];
switch (this.datamanager.getCurrentTool()) {
@@ -120,6 +121,19 @@ export default class extends BaseController {
this.datamanager.setCurrentOptions(imggenHandler.getOptions());
break;
}
+ case 'describeimg':
+ case 'imagetotext': {
+ const fileUploadArea = this.baseElement.querySelector('[data-preference="fileupload"]');
+ if (fileUploadArea) {
+ this.datamanager.getEventEmitterElement().addEventListener('fileUploaded', async (event) => {
+ this.datamanager.setCurrentFile(event.detail.newFile);
+ this.datamanager.setCurrentOptions(ittHandler.getOptions());
+ });
+ }
+ this.datamanager.setCurrentPrompt(ittHandler.getPrompt(this.datamanager.getCurrentTool()));
+ this.datamanager.setCurrentFile(null);
+ break;
+ }
}
if (backButton) {
diff --git a/amd/src/controllers/start.js b/amd/src/controllers/start.js
index f812248..22819e1 100644
--- a/amd/src/controllers/start.js
+++ b/amd/src/controllers/start.js
@@ -42,6 +42,8 @@ export default class extends BaseController {
const audiogenButton = this.baseElement.querySelector('[data-action="loadaudiogen"]');
const imggenButton = this.baseElement.querySelector('[data-action="loadimggen"]');
const freePromptButton = this.baseElement.querySelector('[data-action="loadfreeprompt"]');
+ const describeimgButton = this.baseElement.querySelector('[data-action="loaddescribeimg"]');
+ const imagetotextButton = this.baseElement.querySelector('[data-action="loadimagetotext"]');
const startHandler = getStartHandler(this.uniqid);
@@ -93,6 +95,18 @@ export default class extends BaseController {
await this.renderer.renderImggen();
});
}
+ if (describeimgButton) {
+ describeimgButton.addEventListener('click', async() => {
+ this.datamanager.setCurrentTool('describeimg');
+ await this.renderer.renderDescribeimg();
+ });
+ }
+ if (imagetotextButton) {
+ imagetotextButton.addEventListener('click', async() => {
+ this.datamanager.setCurrentTool('imagetotext');
+ await this.renderer.renderImagetotext();
+ });
+ }
if (freePromptButton) {
if (!freePromptButton.classList.contains('disabled')) {
freePromptButton.addEventListener('click', async () => {
diff --git a/amd/src/datahandler/basedata.js b/amd/src/datahandler/basedata.js
index d15ba9f..3923a04 100644
--- a/amd/src/datahandler/basedata.js
+++ b/amd/src/datahandler/basedata.js
@@ -35,10 +35,15 @@ const stringKeys = [
'backbutton_tooltip',
'cancel',
'deletebutton_tooltip',
+ 'describeimg_baseprompt',
+ 'describeimg_headline',
'describe_baseprompt',
'describe_headline',
'dismiss',
'dismisssuggestion',
+ 'error_nofile',
+ 'error_nofileinclipboard_text',
+ 'error_nofileinclipboard_title',
'error_nopromptgiven',
'freeprompt_placeholder',
'freepromptbutton_tooltip',
@@ -47,6 +52,10 @@ const stringKeys = [
'generate',
'generatebutton_tooltip',
'hideprompt',
+ 'imagefromeditor',
+ 'imagetotext_baseprompt',
+ 'imagetotext_headline',
+ 'imagetotext_insertimage',
'imggen_headline',
'imggen_placeholder',
'insertatcaret',
@@ -75,8 +84,10 @@ const stringKeys = [
'texttouse',
'toolname_audiogen',
'toolname_describe',
+ 'toolname_describeimg',
'toolname_describe_extension',
'toolname_imggen',
+ 'toolname_imagetotext',
'toolname_summarize',
'toolname_summarize_extension',
'toolname_translate',
@@ -110,10 +121,15 @@ export const init = async() => {
strings.backbutton_tooltip,
strings.cancel,
strings.deletebutton_tooltip,
+ strings.describeimg_baseprompt,
+ strings.describeimg_headline,
strings.describe_baseprompt,
strings.describe_headline,
strings.dismiss,
strings.dismisssuggestion,
+ strings.error_nofile,
+ strings.error_nofileinclipboard_text,
+ strings.error_nofileinclipboard_title,
strings.error_nopromptgiven,
strings.freeprompt_placeholder,
strings.freepromptbutton_tooltip,
@@ -122,6 +138,10 @@ export const init = async() => {
strings.generate,
strings.generatebutton_tooltip,
strings.hideprompt,
+ strings.imagefromeditor,
+ strings.imagetotext_baseprompt,
+ strings.imagetotext_headline,
+ strings.imagetotext_insertimage,
strings.imggen_headline,
strings.imggen_placeholder,
strings.insertatcaret,
@@ -150,8 +170,10 @@ export const init = async() => {
strings.texttouse,
strings.toolname_audiogen,
strings.toolname_describe,
+ strings.toolname_describeimg,
strings.toolname_describe_extension,
strings.toolname_imggen,
+ strings.toolname_imagetotext,
strings.toolname_summarize,
strings.toolname_summarize_extension,
strings.toolname_translate,
diff --git a/amd/src/datahandler/itt.js b/amd/src/datahandler/itt.js
new file mode 100644
index 0000000..570fff8
--- /dev/null
+++ b/amd/src/datahandler/itt.js
@@ -0,0 +1,82 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+import * as AiConfig from 'local_ai_manager/config';
+import * as BasedataHandler from 'tiny_ai/datahandler/basedata';
+import BaseHandler from 'tiny_ai/datahandler/base';
+import {getDatamanager} from 'tiny_ai/utils';
+
+/**
+ * Tiny AI data handler for image to text.
+ *
+ * @module tiny_ai/datahandler/itt
+ * @copyright 2024, ISB Bayern
+ * @author Philipp Memmel
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+export default class extends BaseHandler {
+
+ ittOptions = null;
+
+ async loadIttOptions() {
+ if (this.ittOptions === null) {
+ const fetchedOptions = await AiConfig.getPurposeOptions('itt');
+ this.ittOptions = JSON.parse(fetchedOptions.options);
+ }
+ }
+
+ async getAllowedMimetypes() {
+ await this.loadIttOptions();
+ return this.ittOptions.allowedmimetypes;
+ }
+
+ getOptions() {
+ const options = {};
+ const datamanager = getDatamanager(this.uniqid);
+ options.image = datamanager.getCurrentFile();
+ return options;
+ }
+
+ /**
+ * Get the prompt.
+ *
+ * @param {string} tool the tool to generate the prompt for, can be 'describeimage' and 'imagetotext'
+ */
+ getPrompt(tool) {
+ return BasedataHandler.getTinyAiString(tool + '_baseprompt');
+ }
+
+ /**
+ * Get the rendering context.
+ *
+ * @param {string} tool the tool to generate the context for, can be 'describeimage' and 'imagetotext'
+ */
+ async getTemplateContext(tool) {
+ const context = {
+ modal_headline: BasedataHandler.getTinyAiString(tool + '_headline'),
+ showIcon: true,
+ tool: tool,
+ textareatype: 'prompt',
+ placeholder: BasedataHandler.getTinyAiString(tool + '_placeholder'),
+ insertimagedescription: BasedataHandler.getTinyAiString('imagetotext_insertimage')
+ };
+
+ Object.assign(context, BasedataHandler.getShowPromptButtonContext());
+
+ Object.assign(context, BasedataHandler.getBackAndGenerateButtonContext());
+ return context;
+ }
+}
diff --git a/amd/src/datahandler/start.js b/amd/src/datahandler/start.js
index 801fc7b..fd361b4 100644
--- a/amd/src/datahandler/start.js
+++ b/amd/src/datahandler/start.js
@@ -107,9 +107,11 @@ export default class extends BaseHandler {
}
if (mode === constants.modalModes.selection) {
- return ['audiogen', 'imggen'].includes(tool) ? this.strings.error_unavailable_noselection : '';
+ return ['audiogen', 'imggen']
+ .includes(tool) ? this.strings.error_unavailable_noselection : '';
} else if (mode === constants.modalModes.general) {
- return ['summarize', 'translate', 'describe', 'tts'].includes(tool) ? this.strings.error_unavailable_selection : '';
+ return ['summarize', 'translate', 'describe', 'tts']
+ .includes(tool) ? this.strings.error_unavailable_selection : '';
}
return '';
}
@@ -208,6 +210,28 @@ export default class extends BaseHandler {
action: 'loadimggen'
});
}
+ if (!this.isToolHidden('describeimg')) {
+ toolButtons.push({
+ toolname: 'describeimg',
+ tool: BasedataHandler.getTinyAiString('toolname_describeimg'),
+ iconstyle: 'solid',
+ iconname: 'file-image',
+ disabled: this.isToolDisabled('describeimg', mode).length > 0,
+ tooltip: stripHtmlTags(this.isToolDisabled('describeimg', mode)),
+ action: 'loaddescribeimg'
+ });
+ }
+ if (!this.isToolHidden('imagetotext')) {
+ toolButtons.push({
+ toolname: 'imagetotext',
+ tool: BasedataHandler.getTinyAiString('toolname_imagetotext'),
+ iconstyle: 'solid',
+ iconname: 'signature',
+ disabled: this.isToolDisabled('imagetotext', mode).length > 0,
+ tooltip: stripHtmlTags(this.isToolDisabled('imagetotext', mode)),
+ action: 'loadimagetotext'
+ });
+ }
// We sort the not disabled tools to the top while keeping the groups "disabled tools" and "not disabled tools"
// in the same order inside the groups.
toolButtons.sort((a, b) => {
diff --git a/amd/src/datamanager.js b/amd/src/datamanager.js
index ba3be5d..ad18c0e 100644
--- a/amd/src/datamanager.js
+++ b/amd/src/datamanager.js
@@ -36,7 +36,10 @@ export default class DataManager {
currentTool = null;
currentAiResult = null;
prompt = null;
+ file = null;
options = null;
+ selection = null;
+ selectionImg = null;
getDefaultOptions() {
const defaultOptions = {
@@ -75,6 +78,15 @@ export default class DataManager {
}
+ setCurrentFile(file) {
+ this.file = file;
+ }
+
+ getCurrentFile() {
+ return this.file;
+
+ }
+
getSelection() {
return this.selection;
}
@@ -89,6 +101,14 @@ export default class DataManager {
this.selection = selection;
}
+ getSelectionImg() {
+ return this.selectionImg;
+ }
+
+ setSelectionImg(image) {
+ this.selectionImg = image;
+ }
+
getEventEmitterElement() {
return this.eventEmitterElement;
}
@@ -116,9 +136,6 @@ export default class DataManager {
this.setCurrentOptions(null);
this.setCurrentTool(null);
this.setCurrentAiResult(null);
+ this.setCurrentFile(null);
}
}
-
-
-
-
diff --git a/amd/src/editor_utils.js b/amd/src/editor_utils.js
index 479e46f..d1edfed 100644
--- a/amd/src/editor_utils.js
+++ b/amd/src/editor_utils.js
@@ -28,8 +28,8 @@ import {getUserId} from 'tiny_ai/options';
import {constants} from 'tiny_ai/constants';
import {selectionbarSource, toolbarSource, menubarSource} from 'tiny_ai/common';
import {getDraftItemId as getDraftItemIdTinyCore, getContextId as getContextItemIdTinyCore} from 'editor_tiny/options';
-import {getRenderer, getDatamanager} from 'tiny_ai/utils';
-
+import {getRenderer, getDatamanager, errorAlert} from 'tiny_ai/utils';
+import * as BasedataHandler from 'tiny_ai/datahandler/basedata';
export default class {
@@ -65,11 +65,25 @@ export default class {
}
});
this.modal.show();
+ const renderer = getRenderer(this.uniqid);
+ getDatamanager(this.uniqid).setSelectionImg(null);
if (this.mode === constants.modalModes.selection) {
+ const selectedEditorContentHtml = this.editor.selection.getContent({format: 'html'});
+ const parser = new DOMParser();
+ const editorDom = parser.parseFromString(selectedEditorContentHtml, 'text/html');
+ const images = editorDom.querySelectorAll('img');
+
+ if (images.length > 0 && images[0].src) {
+ // If there are more than one we just use the first one.
+ const image = images[0];
+ // This should work for both external and data urls.
+ const fetchResult = await fetch(image.src);
+ const data = await fetchResult.blob();
+ getDatamanager(this.uniqid).setSelectionImg(data);
+ }
getDatamanager(this.uniqid).setSelection(this.editor.selection.getContent());
}
- const renderer = getRenderer(this.uniqid);
// Unfortunately, the modal will not execute any JS code in the template, so we need to rerender the modal as a whole again.
await renderer.renderStart();
this.modal.getRoot().on(ModalEvents.outsideClick, event => {
diff --git a/amd/src/renderer.js b/amd/src/renderer.js
index 4a0d2c2..a80135a 100644
--- a/amd/src/renderer.js
+++ b/amd/src/renderer.js
@@ -35,7 +35,8 @@ import {
getStartHandler,
getSummarizeHandler,
getTranslateHandler,
- getTtsHandler
+ getTtsHandler,
+ getIttHandler
} from 'tiny_ai/utils';
export default class {
@@ -89,6 +90,16 @@ export default class {
await this.renderModalContent('moodle-modal-body-mediageneration', 'moodle-modal-footer-generate', templateContext);
}
+ async renderDescribeimg() {
+ const templateContext = await getIttHandler(this.uniqid).getTemplateContext('describeimg');
+ await this.renderModalContent('moodle-modal-body-itt', 'moodle-modal-footer-generate', templateContext);
+ }
+
+ async renderImagetotext() {
+ const templateContext = await getIttHandler(this.uniqid).getTemplateContext('imagetotext');
+ await this.renderModalContent('moodle-modal-body-itt', 'moodle-modal-footer-generate', templateContext);
+ }
+
async renderLoading() {
const templateContext = {};
templateContext.modal_headline = BasedataHandler.getTinyAiString('aigenerating');
@@ -220,14 +231,14 @@ export default class {
const infoBoxSelector = '[data-rendertarget="infobox"]';
if (document.querySelector(infoBoxSelector)) {
await renderInfoBox('tiny_ai', getEditorUtils(this.uniqid).getUserId(), infoBoxSelector,
- ['singleprompt', 'translate', 'tts', 'imggen']);
+ ['singleprompt', 'translate', 'tts', 'imggen', 'itt']);
}
}
async insertUserQuotaBox() {
const usageBoxSelector = '[data-rendertarget="usageinfo"]';
if (document.querySelector(usageBoxSelector)) {
- await renderUserQuota(usageBoxSelector, ['singleprompt', 'translate', 'tts', 'imggen']);
+ await renderUserQuota(usageBoxSelector, ['singleprompt', 'translate', 'tts', 'imggen', 'itt']);
}
}
}
diff --git a/amd/src/utils.js b/amd/src/utils.js
index 511637c..7358d0c 100644
--- a/amd/src/utils.js
+++ b/amd/src/utils.js
@@ -31,6 +31,7 @@ import StartHandler from 'tiny_ai/datahandler/start';
import SummarizeHandler from 'tiny_ai/datahandler/summarize';
import TranslateHandler from 'tiny_ai/datahandler/translate';
import TtsHandler from 'tiny_ai/datahandler/tts';
+import IttHandler from 'tiny_ai/datahandler/itt';
import {alert as Alert, exception as displayException} from 'core/notification';
import {getString} from 'core/str';
import {makeRequest} from 'local_ai_manager/make_request';
@@ -56,6 +57,7 @@ export const init = async (uniqid, editor) => {
objectStore[uniqid].summarizehandler = new SummarizeHandler(uniqid);
objectStore[uniqid].translatehandler = new TranslateHandler(uniqid);
objectStore[uniqid].ttshandler = new TtsHandler(uniqid);
+ objectStore[uniqid].itthandler = new IttHandler(uniqid);
objectStore[uniqid].renderer = new Renderer(uniqid);
}
};
@@ -135,6 +137,10 @@ export const getTtsHandler = (uniqid) => {
return objectStore[uniqid].ttshandler;
};
+export const getIttHandler = (uniqid) => {
+ return objectStore[uniqid].itthandler;
+};
+
export const getCurrentModalUniqId = (element) => {
return element.closest('[data-tiny_instance_uniqid]').dataset.tiny_instance_uniqid;
};
diff --git a/lang/de/tiny_ai.php b/lang/de/tiny_ai.php
index e491af1..ac5d78b 100644
--- a/lang/de/tiny_ai.php
+++ b/lang/de/tiny_ai.php
@@ -38,11 +38,18 @@
$string['deletebutton_tooltip'] = 'Aktuelles Ergebnis verwerfen und zurück zur Einstellungsseite';
$string['describe_headline'] = 'Ausführliche Beschreibung des markierten Texts';
$string['describe_baseprompt'] = 'Beschreibe den nachfolgenden Text';
+$string['describeimg_headline'] = 'Bildanalyse';
+$string['describeimg_baseprompt'] = 'Beschreibe, was auf dem Bild zu sehen ist';
$string['dismiss'] = 'Verwerfen';
$string['dismisssuggestion'] = 'Möchten Sie den KI-Vorschlag verwerfen?';
$string['errorwithcode'] = 'Ein Fehler ist aufgetreten, Fehlercode: {$a}';
+$string['error_nofile'] = 'Keine Datei eingefügt. Bitte Datei hinzufügen.';
+$string['error_nofileinclipboard_text'] = 'Die Zwischenablage enthält keine Datei. Bitte eine Datei/einen Screenshot in die Zwischenablage einfügen.';
+$string['error_nofileinclipboard_title'] = 'Keine Datei';
$string['error_nopromptgiven'] = 'Kein Prompt angegeben. Bitte einen Prompt eintippen oder einfügen.';
$string['error_tiny_ai_notavailable'] = 'Die KI-Funktionen stehen Ihnen nicht zur Verfügung.';
+$string['error_unsupportedfiletype_text'] = 'Der Dateityp wird nicht unterstützt. unterstützte Typen sind: {$a}';
+$string['error_unsupportedwrongfiletype_title'] = 'Nicht unterstützter Dateityp';
$string['freepromptbutton_tooltip'] = 'Generiere KI-Antwort';
$string['freeprompt_placeholder'] = 'Geben Sie der KI eine beliebige Anweisung zur Textgenerierung...';
$string['gender'] = 'Geschlecht';
@@ -51,6 +58,10 @@
$string['generatebutton_tooltip'] = 'KI eine Antwort generieren lassen';
$string['generating'] = 'Die KI-Antwort wird generiert...';
$string['hideprompt'] = 'Prompt ausblenden';
+$string['imagefromeditor'] = 'Bild aus Editor';
+$string['imagetotext_headline'] = 'Handschrifterkennung';
+$string['imagetotext_baseprompt'] = 'Gib den Text auf dem Bild zurück';
+$string['imagetotext_insertimage'] = 'Ziehen Sie eine Datei per Drag&Drop in diesen Bereich oder fügen Sie sie aus der Zwischenablage ein';
$string['imggen_headline'] = 'Bildgenerierung';
$string['imggen_placeholder'] = 'Beschreibung des Bilds hier eingeben oder einfügen, z. B. "Generiere ein fotorealistisches Bild eines Affen mit einem Bleistift in der Hand und einem Hut auf dem Kopf"';
$string['insertatcaret'] = 'An aktueller Position einfügen';
@@ -89,7 +100,9 @@
$string['toolname_audiogen'] = 'Audiogenerierung';
$string['toolname_describe'] = 'Ausführliche Beschreibung';
$string['toolname_describe_extension'] = 'des markierten Textes';
+$string['toolname_describeimg'] = 'Bildbeschreibung';
$string['toolname_imggen'] = 'Bildgenerierung';
+$string['toolname_imagetotext'] = 'Handschrifterkennung';
$string['toolname_summarize'] = 'Zusammenfassen';
$string['toolname_summarize_extension'] = 'des markierten Textes';
$string['toolname_translate'] = 'Übersetzen';
diff --git a/lang/en/tiny_ai.php b/lang/en/tiny_ai.php
index ee4f64c..9bbd63c 100644
--- a/lang/en/tiny_ai.php
+++ b/lang/en/tiny_ai.php
@@ -37,12 +37,19 @@
$string['cancel'] = 'Cancel';
$string['deletebutton_tooltip'] = 'Dismiss current result and go back to the preferences page';
$string['describe_headline'] = 'Detailed description of the selected text';
+$string['describe_baseprompt'] = 'Describe the following text';
+$string['describeimg_headline'] = 'Image analysis';
+$string['describeimg_baseprompt'] = 'Describe what is being shown on the image';
$string['dismiss'] = 'Dismiss';
$string['dismisssuggestion'] = 'Do you want to dismiss the AI suggestion?';
-$string['describe_baseprompt'] = 'Describe the following text';
$string['errorwithcode'] = 'An error occured with error code {$a}';
+$string['error_nofile'] = 'No file added. Please add a file.';
+$string['error_nofileinclipboard_text'] = 'Clipboard does not contain file data. Please copy a file into the clipboard before pasting.';
+$string['error_nofileinclipboard_title'] = 'No file';
$string['error_nopromptgiven'] = 'No prompt given. Please insert a prompt.';
$string['error_tiny_ai_notavailable'] = 'The AI features are not availeble for you.';
+$string['error_unsupportedfiletype_text'] = 'This file type is not supported. Supported types are: {$a}';
+$string['error_unsupportedfiletype_title'] = 'Not supported filetype';
$string['freepromptbutton_tooltip'] = 'Generate AI answer';
$string['freeprompt_placeholder'] = 'Give the AI any order for generating text...';
$string['gender'] = 'Gender';
@@ -51,6 +58,10 @@
$string['generatebutton_tooltip'] = 'Let the AI generate an answer';
$string['generating'] = 'The AI answer is being generated...';
$string['hideprompt'] = 'Hide prompt';
+$string['imagefromeditor'] = 'Image from editor';
+$string['imagetotext_headline'] = 'Handwriting recognition';
+$string['imagetotext_baseprompt'] = 'Parse the text in the image';
+$string['imagetotext_insertimage'] = 'Drag&Drop a file into this area or paste a file from clipboard';
$string['imggen_headline'] = 'Image generation';
$string['imggen_placeholder'] = 'Insert or paste description of the image here, for example "Generate a photorealistic image of a monkey that holds a pen in one hand and wears a hat on the head"';
$string['insertatcaret'] = 'Insert at current position';
@@ -89,7 +100,9 @@
$string['toolname_audiogen'] = 'Audio generation';
$string['toolname_describe'] = 'Detailed description';
$string['toolname_describe_extension'] = 'of the selected text';
+$string['toolname_describeimg'] = 'Image description';
$string['toolname_imggen'] = 'Image generation';
+$string['toolname_imagetotext'] = 'Handwriting recognition';
$string['toolname_summarize'] = 'Summarize';
$string['toolname_summarize_extension'] = 'the selected text';
$string['toolname_translate'] = 'Translate';
diff --git a/scss/partials/ai-dropzone.scss b/scss/partials/ai-dropzone.scss
new file mode 100644
index 0000000..e6dc049
--- /dev/null
+++ b/scss/partials/ai-dropzone.scss
@@ -0,0 +1,32 @@
+.tiny_ai_dropzone {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ border-width: 2px;
+ border-radius: 2px;
+ border-color: #eeeeee;
+ border-style: dashed;
+ background-color: #fafafa;
+ color: #bdbdbd;
+ outline: none;
+ transition: border .24s ease-in-out;
+ height: 15rem;
+ /* Hide the cursor, because we need to set contenteditable on the div for pasting, but do not want editing */
+ caret-color: transparent;
+}
+
+.tiny_ai_dragover {
+ background-color: #63676c;
+}
+
+.tiny_ai_dropzone_filled {
+ background-color: #ffffff;
+ color: #000000;
+}
+
+.tiny_ai_dropzone_preview_container {
+ max-width: 80%;
+ max-height: 80%;
+}
diff --git a/styles.css b/styles.css
index 35d3ee9..31913b5 100644
--- a/styles.css
+++ b/styles.css
@@ -567,6 +567,39 @@
color: #00091E;
}
+.tiny_ai_dropzone {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ border-width: 2px;
+ border-radius: 2px;
+ border-color: #eeeeee;
+ border-style: dashed;
+ background-color: #fafafa;
+ color: #bdbdbd;
+ outline: none;
+ transition: border 0.24s ease-in-out;
+ height: 15rem;
+ /* Hide the cursor, because we need to set contenteditable on the div for pasting, but do not want editing */
+ caret-color: transparent;
+}
+
+.tiny_ai_dragover {
+ background-color: #63676c;
+}
+
+.tiny_ai_dropzone_filled {
+ background-color: #ffffff;
+ color: #000000;
+}
+
+.tiny_ai_dropzone_preview_container {
+ max-width: 80%;
+ max-height: 80%;
+}
+
.tiny_ai-alert {
display: flex;
align-items: center;
diff --git a/styles.scss b/styles.scss
index 40a4b2a..7f4e4d8 100644
--- a/styles.scss
+++ b/styles.scss
@@ -7,6 +7,7 @@
@import 'scss/partials/ai-modal.scss';
@import 'scss/partials/ai-button.scss';
@import 'scss/partials/ai-dropdown.scss';
+@import 'scss/partials/ai-dropzone.scss';
@import 'scss/partials/ai-info-box.scss';
@import 'scss/partials/ai-icon-button.scss';
@import 'scss/partials/ai-result-text.scss';
diff --git a/templates/components/ai-file-list-entry.mustache b/templates/components/ai-file-list-entry.mustache
new file mode 100644
index 0000000..3bd4862
--- /dev/null
+++ b/templates/components/ai-file-list-entry.mustache
@@ -0,0 +1,6 @@
+
+
{{filename}}
+ {{#isImage}}
+
+ {{/isImage}}
+
diff --git a/templates/components/moodle-modal-body-itt.mustache b/templates/components/moodle-modal-body-itt.mustache
new file mode 100644
index 0000000..608b293
--- /dev/null
+++ b/templates/components/moodle-modal-body-itt.mustache
@@ -0,0 +1,25 @@
+
+{{#js}}
+ require(['tiny_ai/controllers/preferences', 'tiny_ai/controllers/promptedit', 'tiny_ai/controllers/file'], function(Controller, PromptEditController, FileController) {
+ const controller = new Controller('#tiny_ai_{{tool}}_{{uniqid}}', '{{tool}}');
+ Promise.resolve(controller.init());
+ const promptEditController = new PromptEditController('#tiny_ai_{{tool}}_{{uniqid}}');
+ Promise.resolve(promptEditController.init());
+ const fileController = new FileController('#tiny_ai_{{tool}}_{{uniqid}}');
+ Promise.resolve(fileController.init());
+ });
+{{/js}}