/*
* Copyright 2025 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class provides a widget to generate an image from a text using AI.
* See {@link Ametys.form.widget.File.FileSource}.
*/
Ext.define('Ametys.plugins.ai.widget.File.AIGenerated', {
extend: 'Ametys.form.widget.File.FileSource',
statics: {
/**
* @property {String} SOURCE The file source which belongs to this class
* @static
* @readonly
*/
SOURCE: 'AIGenerated',
/**
* @property {String} TYPE The file type which belongs to this class
* @static
* @readonly
*/
TYPE: 'attribute',
/**
* The filters configuration
* @static
* @private
*/
filters: {
none: {
buttonText: "",
buttonTooltip: "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_TOOLTIP}}",
buttonIconCls: 'ametysicon-sparkles',
menuItemText: "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_LABEL}}",
menuItemIconCls: 'ametysicon-sparkles',
dialogTitle: "{{i18n PLUGINS_CORE_UI_WIDGET_RESOURCES_PICKER_INSERT_LOCAL_IMAGE}}",
dialogHint: "{{i18n PLUGINS_CORE_UI_WIDGET_RESOURCES_PICKER_INSERT_LOCAL_IMAGE_HINT}}",
filter: null
},
image: {
buttonText: "",
buttonTooltip: "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_TOOLTIP}}",
buttonIconCls: 'ametysicon-sparkles',
menuItemText: "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_LABEL}}",
menuItemIconCls: 'ametysicon-sparkles',
dialogTitle: "{{i18n PLUGINS_CORE_UI_WIDGET_RESOURCES_PICKER_INSERT_LOCAL_IMAGE}}",
dialogHint: "{{i18n PLUGINS_CORE_UI_WIDGET_RESOURCES_PICKER_INSERT_LOCAL_IMAGE_HINT}}",
filter: Ametys.helper.FileUpload.IMAGE_FILTER
}
}
},
getFileType: function ()
{
return this.self.TYPE;
},
getBtnConfig: function (config, filter)
{
filter = filter || 'none';
return {
text: config.buttonText || this.self.filters[filter].buttonText,
tooltip: config.buttonTooltip || this.self.filters[filter].buttonTooltip,
iconCls: config.buttonIconCls || this.self.filters[filter].buttonIconCls,
source: this.self.SOURCE
};
},
getMenuItemConfig: function (config, filter)
{
filter = filter || 'none';
if (!this.self.filters[filter])
{
this.getLogger().error("The filter '" + filter + "' does not exists. Switching to 'none'.")
filter = 'none';
}
return {
text: this.self.filters[filter].menuItemText,
iconCls: this.self.filters[filter].menuItemIconCls,
source: this.self.SOURCE
}
},
handler: function (config, filter, allowedExtensions, callback, widget)
{
let me = this;
let text = me._getTextToSummarize(widget);
Ametys.Msg.confirm(
"{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_ALERT_TITLE}}",
"{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_ALERT_TEXT}}".replace("\{0\}", this._getFieldsLabelsToSummarize(widget)),
function(btn) {
if (btn === 'yes')
{
if (!text)
{
Ametys.Msg.alert("{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_EMPTY_ERROR_TITLE}}", "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_EMPTY_ERROR_TEXT}}");
}
else
{
me._generatesImage(text, widget.config.shortName, widget, callback);
}
}
}
);
},
_generatesImage: function(text, name, widget, callback)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.plugins.ai.AIHelper",
methodName: "textToImage",
parameters: [text, name],
callback: {
handler: function (result) {
if (result && result.error)
{
Ametys.Msg.alert("{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_GENERATION_ERROR_TITLE}}", "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_GENERATION_ERROR_TEXT}}");
}
else
{
Ext.create('Ametys.helper.CropDialog', {
imageId: result.id,
imageName: result.filename,
imageSize: result.size,
imageViewHref: Ametys.CONTEXT_PATH + result.viewHref,
imageDownloadHref: Ametys.CONTEXT_PATH + result.downloadHref,
cropUrl: undefined,
afterCropFn: callback,
actionResult: result
}).show();
}
},
scope: this
},
waitMessage: {
msg: "{{i18n PLUGINS_AI_WIDGET_IMAGE_SUMMARY_RUNNING}}",
target: widget,
},
errorMessage: true
});
},
_getFieldsLabelsToSummarize: function(widget)
{
function _removeMandatory(char)
{
if (char && char.startsWith("* "))
{
return char.substring(2);
}
else if (char && char.endsWith(" *"))
{
return char.substring(0, char.length - 2);
}
else
{
return char;
}
}
let v = this._applyToAllDependencies(widget, function (id) {return _removeMandatory(widget.form.getRelativeField(id, widget).getFieldLabel())});
v = v ? v.join("</li><li>") : null;
return v ? "<ul><li>" + v + "</li></ul>" : "-";
},
/**
* Get the text to summarize from the configured attribute paths
* @return {String} The concatenated text
*/
_getTextToSummarize: function(widget)
{
let me = this;
let lines = this._applyToAllDependencies(widget, function (id) {return Ametys.form.Widget.getRelativeValue(id, widget)});
return lines ? lines.join("\n") : null;
},
_applyToAllDependencies: function(widget, f)
{
let summarize = widget.summarize;
if (!summarize)
{
return null;
}
if (!Ext.isArray(summarize))
{
summarize = summarize.split(',');
}
return summarize.map(id => f(id.trim()));
}
});
Ametys.form.widget.File.registerFileSource (Ametys.plugins.ai.widget.File.AIGenerated.SOURCE, Ext.create('Ametys.plugins.ai.widget.File.AIGenerated', {}));