diff --git a/sdkjs-plugins/content/ai/resources/styles/customAssistant.css b/sdkjs-plugins/content/ai/resources/styles/customAssistant.css index 4b3ccf77..b3980001 100644 --- a/sdkjs-plugins/content/ai/resources/styles/customAssistant.css +++ b/sdkjs-plugins/content/ai/resources/styles/customAssistant.css @@ -43,6 +43,21 @@ label { text-align: center; } +#warning_text { + display: flex; + flex-direction: row; + padding-top: 8px; + gap: 12px; + line-height: 16px; +} + #warning_text svg { + margin-top: 7px; + } + #warning_text p { + width: calc(100% - 76px); + } + + #custom_assistant { height: auto; padding: 14px 0 8px 0; diff --git a/sdkjs-plugins/content/ai/scripts/code.js b/sdkjs-plugins/content/ai/scripts/code.js index 3dcfb70c..710480b5 100644 --- a/sdkjs-plugins/content/ai/scripts/code.js +++ b/sdkjs-plugins/content/ai/scripts/code.js @@ -30,6 +30,8 @@ * */ +/// + let settingsWindow = null; let aiModelsListWindow = null; let aiModelEditWindow = null; @@ -961,7 +963,7 @@ async function onCheckGrammarSpelling(isCurrent) function customAssistantWindowShow(assistantId, buttonAssistant) { if (window.customAssistantWindow) { - closeCustomAssistantWindow(); + customAssistantWindowClose(); } const actionButtonText = assistantId ? 'Save' : 'Create'; const description = assistantId ? 'Edit' : 'Create a new assistant'; @@ -1025,7 +1027,7 @@ function customAssistantWindowShow(assistantId, buttonAssistant) Asc.Buttons.updateToolbarMenu(window.buttonMainToolbar.id, window.buttonMainToolbar.name, [buttonAssistant]); customAssistantManager.createAssistant(element); } - closeCustomAssistantWindow(); + customAssistantWindowClose(); } else { await window.pluginsButtonsCallback(id, windowId, ...args); } @@ -1034,7 +1036,7 @@ function customAssistantWindowShow(assistantId, buttonAssistant) window.customAssistantWindow = customAssistantWindow; } -function closeCustomAssistantWindow() { +function customAssistantWindowClose() { if (window.customAssistantWindow) { window.customAssistantWindow.close(); window.customAssistantWindow = null; @@ -1050,7 +1052,7 @@ function closeCustomAssistantWindow() { */ function customAssistantWindowDeleteConfirm(assistantId, buttonAssistant) { if (window.customAssistantWindow) { - closeCustomAssistantWindow(); + customAssistantWindowClose(); } const savedAssistants = JSON.parse( @@ -1101,7 +1103,50 @@ if (window.customAssistantWindow) { } } } - closeCustomAssistantWindow(); + customAssistantWindowClose(); + } else { + await window.pluginsButtonsCallback(id, windowId, ...args); + } + } + + window.customAssistantWindow = customAssistantWindow; +} +/** + * @param {localStorageCustomAssistantItem} assistantData + * @param {string} warningText + */ +function customAssistantWarning(assistantData, warningText) { +if (window.customAssistantWindow) { + customAssistantWindowClose(); + } + + let variation = { + url : "customAssistant.html", + description : window.Asc.plugin.tr('Warning!'), + isVisual : true, + buttons : [ + { text: window.Asc.plugin.tr('Ok'), primary: true }, + ], + isModal : true, + isCanDocked: false, + type: "window", + EditorsSupport : ["word"], + size : [ 350, 76 ] + }; + + const customAssistantWindow = new window.Asc.PluginWindow(); + customAssistantWindow.attachEvent("onWindowReady", function() { + Asc.Editor.callMethod("ResizeWindow", [customAssistantWindow.id, [350, 76], [350, 76], [0, 0]]); + customAssistantWindow.command('onWarningAssistant', warningText); + + }); + + customAssistantWindow.show(variation); + + window.pluginsButtonsCallback = window.Asc.plugin.button; + window.Asc.plugin.button = async function(id, windowId, ...args) { + if (customAssistantWindow && windowId === customAssistantWindow.id) { + customAssistantWindowClose(); } else { await window.pluginsButtonsCallback(id, windowId, ...args); } diff --git a/sdkjs-plugins/content/ai/scripts/customAssistant.js b/sdkjs-plugins/content/ai/scripts/customAssistant.js index dd3fda31..f7b9a31e 100644 --- a/sdkjs-plugins/content/ai/scripts/customAssistant.js +++ b/sdkjs-plugins/content/ai/scripts/customAssistant.js @@ -110,6 +110,20 @@ } ); + window.Asc.plugin.attachEvent( + "onWarningAssistant", + (/** @type {string} */ warningText) => { + const image = '' + + '' + + '' + + '' + + ''; + const text = '
' + + image + '

' + window.Asc.plugin.tr(warningText) + '

' + mainContainer.innerHTML = image + text; + } + ); + function onThemeChanged(theme) { window.Asc.plugin.onThemeChangedBase(theme); updateBodyThemeClasses(theme.type, theme.name); diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/custom-annotator.js b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/custom-annotator.js index 50e376e3..dbf978c2 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/custom-annotator.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/custom-annotator.js @@ -51,6 +51,7 @@ Object.assign(CustomAnnotator.prototype, { * @param {string} paraId * @param {string} recalcId * @param {string} text + * @returns {Promise} */ annotateParagraph: async function (paraId, recalcId, text) { this.paragraphs[paraId] = {}; @@ -60,7 +61,10 @@ Object.assign(CustomAnnotator.prototype, { const argPrompt = this._createPrompt(text); let response = await this.chatRequest(argPrompt); - if (!response) return false; + + if (!response || response === '[]') { + return false; + } try { const ranges = this._convertToRanges(paraId, text, JSON.parse(response)); @@ -73,6 +77,8 @@ Object.assign(CustomAnnotator.prototype, { }; await Asc.Editor.callMethod("AnnotateParagraph", [obj]); } catch (e) {} + + return true; }, /** * @param {string} paraId @@ -103,18 +109,19 @@ Object.assign(CustomAnnotator.prototype, { if (annot["original"] !== text.substring(start, start + len)) { let annotRange = this.getAnnotationRangeObj(paraId, rangeId); - Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]); + return Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]); } }, /** * @param {string[]} paraIds + * @returns {Promise} */ checkParagraphs: async function (paraIds) { if (this._skipNextChangeParagraph) { this._skipNextChangeParagraph = false; - return; + return paraIds.map(() => false); } - TextAnnotator.prototype.checkParagraphs.call(this, paraIds); + return await TextAnnotator.prototype.checkParagraphs.call(this, paraIds); }, onAccept: async function (paraId, rangeId) { this._skipNextChangeParagraph = true; diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/manager.js b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/manager.js index 460338f5..35390d62 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/manager.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/manager.js @@ -140,7 +140,7 @@ class CustomAssistantManager { * @param {string} assistantId * @param {string[]} paraIds */ - run(assistantId, paraIds) { + async run(assistantId, paraIds) { const assistant = this._customAssistants.get(assistantId); if (!assistant) { console.error("Custom assistant not found: " + assistantId); @@ -148,17 +148,32 @@ class CustomAssistantManager { } if (!this._isCustomAssistantInit.get(assistantId)) { + /** @type {Promise[]} */ + const promises = []; this._paragraphsStack.forEach((value, paraId) => { - assistant.onChangeParagraph( + const promise = assistant.onChangeParagraph( paraId, value.recalcId, value.text, value.annotations, ); + promises.push(promise); }); + await Promise.all(promises); + } + + /** @type {boolean[]} */ + const isParagraphsChecked = await assistant.checkParagraphs(paraIds); + + if ( + isParagraphsChecked && + isParagraphsChecked.length && + isParagraphsChecked.every(isDone => !isDone) + ) { + const warningMessage = "Not able to perform this action. Please use prompts related to text analysis, editing, or formatting."; + customAssistantWarning(assistant.assistantData, warningMessage); } - assistant.checkParagraphs(paraIds); this._isCustomAssistantInit.set(assistantId, true); } diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js b/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js index 3e89196c..378e8fad 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js @@ -50,19 +50,21 @@ function TextAnnotator(annotatorPopup) * @param {string} recalcId * @param {string} text * @param {string[]} ranges + * @returns {Promise} */ TextAnnotator.prototype.onChangeParagraph = async function(paraId, recalcId, text, ranges) { - this._handleNewRanges(ranges, paraId, text); + await this._handleNewRanges(ranges, paraId, text); this.waitParagraphs[paraId] = { recalcId : recalcId, text : text }; - this._checkParagraph(paraId); + return this._checkParagraph(paraId); }; /** * @param {string[]} paraIds + * @returns {Promise} */ TextAnnotator.prototype.checkParagraphs = async function(paraIds) { @@ -73,12 +75,19 @@ TextAnnotator.prototype.checkParagraphs = async function(paraIds) _t.paraToCheck.add(paraId); }); - this.paraToCheck.forEach(paraId => this._checkParagraph(paraId)); + /** @type {Promise[]} */ + const promises = []; + this.paraToCheck.forEach(paraId => {promises.push(this._checkParagraph(paraId))}); + return Promise.all(promises); }; +/** + * @param {string} paraId + * @returns {Promise} + */ TextAnnotator.prototype._checkParagraph = async function(paraId) { if (!this.paraToCheck.has(paraId) || !this.waitParagraphs[paraId]) - return; + return false; let recalcId = this.waitParagraphs[paraId].recalcId; let text = this.waitParagraphs[paraId].text; @@ -88,12 +97,14 @@ TextAnnotator.prototype._checkParagraph = async function(paraId) range["rangeId"] = undefined; range["all"] = true; await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); - await this.annotateParagraph(paraId, recalcId, text); + const isAnnotate = await this.annotateParagraph(paraId, recalcId, text); delete this.waitParagraphs[paraId]; this.paraToCheck.delete(paraId); this.checked.add(paraId); + + return isAnnotate; }; TextAnnotator.prototype.annotateParagraph = async function(paraId, recalcId, text) { @@ -174,17 +185,18 @@ TextAnnotator.prototype.resetCurrentRange = function() this.paraId = null; this.rangeId = null; }; -TextAnnotator.prototype._handleNewRanges = function(ranges, paraId, text) +TextAnnotator.prototype._handleNewRanges = async function(ranges, paraId, text) { if (!ranges || !Array.isArray(ranges)) return; - - ranges.forEach(range => this._handleNewRangePositions(range, paraId, text)); - // ↓↓↓ TODO: the cycle seems to make no sense ↓↓↓ + const promises = []; + for (let i = 0; i < ranges.length; ++i) { - this._handleNewRangePositions(ranges[i]); + promises[i] = this._handleNewRangePositions(ranges[i], paraId, text); } + + return Promise.all(promises); }; TextAnnotator.prototype._handleNewRangePositions = function(range, paraId, text) {