From 26a3687951bf7caf4c83fa1732c86eea8c821dd3 Mon Sep 17 00:00:00 2001 From: Artur Date: Fri, 23 Jan 2026 17:24:46 +0300 Subject: [PATCH] refactoring --- .../custom-annotations/assistant-hint.js | 294 ++++++++---------- .../assistant-replace-hint.js | 242 +++++++------- .../custom-annotations/assistant-replace.js | 222 ++++++------- .../custom-annotations/custom-annotator.js | 129 ++++---- .../custom-annotations/manager.js | 106 ++++--- .../text-annotations/text-annotator.js | 4 +- 6 files changed, 471 insertions(+), 526 deletions(-) diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-hint.js b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-hint.js index 3cbf5801..708e2baa 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-hint.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-hint.js @@ -38,186 +38,156 @@ * @constructor * @extends CustomAnnotator */ -function AssistantHint(annotationPopup, assistantData) -{ - CustomAnnotator.call(this, annotationPopup, assistantData); +function AssistantHint(annotationPopup, assistantData) { + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantHint.prototype = Object.create(CustomAnnotator.prototype); AssistantHint.prototype.constructor = AssistantHint; -/** - * @param {string} paraId - * @param {string} recalcId - * @param {string} text - */ -AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, text) -{ - this.paragraphs[paraId] = {}; +Object.assign(AssistantHint.prototype, { + /** + * @param {string} text + * @param {Array} matches + */ + _convertToRanges: function (paraId, text, matches) { + const _t = this; + let rangeId = 1; + const ranges = []; + for (const { + origin, + reason, + paragraph, + occurrence, + confidence, + } of matches) { + if (confidence <= 0.7) continue; - if (text.length === 0) - return false; + let count = 0; + let searchStart = 0; - const argPrompt = this._createPrompt(text); + while (searchStart < text.length) { + const index = _t.simpleGraphemeIndexOf( + text, + origin, + searchStart, + ); + if (index === -1) break; - let response = await this.chatRequest(argPrompt); - if (!response) - return false; - - let rangeId = 1; - let ranges = []; + count++; + if (count === occurrence) { + ranges.push({ + start: index, + length: [...origin].length, + id: rangeId, + }); + _t.paragraphs[paraId][rangeId] = { + original: origin, + reason: reason, + }; + ++rangeId; + break; + } + searchStart = index + 1; + } + } + return ranges; + }, - let _t = this; + /** + * @param {string} text + * @returns {string} + */ + _createPrompt: function (text) { + let prompt = `You are a multi-disciplinary text analysis assistant. + Your task is to find text fragments that match the user's criteria. + + MANDATORY RULES: + 1. Analyze ONLY the provided text. + 2. Find words, phrases, or sentences that match the user's criteria. + 3. For EACH match you find: + - Provide the exact quote. + - Explain WHY it matches the criteria. + - Provide position information (paragraph number). + 4. If no matches are found, return an empty array: []. + 5. Format your response STRICTLY in JSON format. + 6. Support multiple languages (English, Russian, etc.) - /** - * @param {string} text - * @param {Array} matches - */ - function convertToRanges(text, matches) - { - for (const { origin, reason, paragraph, occurrence, confidence } of matches) - { - if (confidence <= 0.7) - continue; - - let count = 0; - let searchStart = 0; - - while (searchStart < text.length) + Response format - return ONLY this JSON array with no additional text: + [ { - const index = _t.simpleGraphemeIndexOf(text, origin, searchStart); - if (index === -1) break; - - count++; - if (count === occurrence) - { - ranges.push({ - "start": index, - "length": [...origin].length, - "id": rangeId - }); - _t.paragraphs[paraId][rangeId] = { - "original" : origin, - "reason" : reason - }; - ++rangeId; - break; - } - searchStart = index + 1; + "origin": "exact text fragment that matches the query", + "reason": "detailed explanation why it matches the criteria", + "paragraph": paragraph_number, + "occurrence": 1, + "confidence": 0.95 } - } - } + ] - try - { - convertToRanges(text, JSON.parse(response)); - let obj = { - "type": "highlightText", - "paragraphId": paraId, - "name" : "customAssistant_" + this.assistantData.id, - "recalcId": recalcId, - "ranges": ranges - }; - await Asc.Editor.callMethod("AnnotateParagraph", [obj]); - } - catch (e) - { } -} + Guidelines for each field: + - "origin": EXACT UNCHANGED original text fragment. Do not fix anything in this field. + - "reason": Clear explanation of why this fragment matches the criteria; IF the user's request contains words like "source", "reference", "link", "cite", "website", "URL", "Wikipedia", "proof", "evidence", "verify" - then you MUST include actual working links in your explanations in html format. + - "paragraph": Paragraph number where the fragment is found (0-based index) + - "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.) + - "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain) + + CRITICAL + - Output should be in the exact this format + - No any comments are allowed -/** - * @param {string} text - * @returns {string} - */ -AssistantHint.prototype._createPrompt = function(text) { - let prompt = `You are a multi-disciplinary text analysis assistant. - Your task is to find text fragments that match the user's criteria. - - MANDATORY RULES: - 1. Analyze ONLY the provided text. - 2. Find words, phrases, or sentences that match the user's criteria. - 3. For EACH match you find: - - Provide the exact quote. - - Explain WHY it matches the criteria. - - Provide position information (paragraph number). - 4. If no matches are found, return an empty array: []. - 5. Format your response STRICTLY in JSON format. - 6. Support multiple languages (English, Russian, etc.) + CRITICAL - Output Format: + - Return ONLY the raw JSON array, nothing else + - DO NOT wrap the response in markdown code blocks (no \`\`\`json or \`\`\`) + - DO NOT include any explanatory text before or after the JSON + - DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible + - The response should start with [ and end with ] + `; + prompt += + "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - Response format - return ONLY this JSON array with no additional text: - [ - { - "origin": "exact text fragment that matches the query", - "reason": "detailed explanation why it matches the criteria", - "paragraph": paragraph_number, - "occurrence": 1, - "confidence": 0.95 - } - ] + prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; - Guidelines for each field: - - "origin": EXACT UNCHANGED original text fragment. Do not fix anything in this field. - - "reason": Clear explanation of why this fragment matches the criteria; IF the user's request contains words like "source", "reference", "link", "cite", "website", "URL", "Wikipedia", "proof", "evidence", "verify" - then you MUST include actual working links in your explanations in html format. - - "paragraph": Paragraph number where the fragment is found (0-based index) - - "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.) - - "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain) - - CRITICAL - - Output should be in the exact this format - - No any comments are allowed + prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; - CRITICAL - Output Format: - - Return ONLY the raw JSON array, nothing else - - DO NOT wrap the response in markdown code blocks (no \`\`\`json or \`\`\`) - - DO NOT include any explanatory text before or after the JSON - - DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible - - The response should start with [ and end with ] - `; - prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - - prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; + return prompt; + }, - prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; + /** + * @param {string} paraId + * @param {string} rangeId + * @return {HintInfoForPopup} + */ + getInfoForPopup: function (paraId, rangeId) { + let _s = this.getAnnotation(paraId, rangeId); + let reason = _s["reason"]; + try { + reason = reason.replace(//gi, ''); + } catch (e) { + console.error(e); + } + return { + original: _s["original"], + explanation: reason, + type: this.type, + }; + }, - return prompt; -} + /** + * @param {string} paraId + * @param {string} rangeId + */ + onAccept: async function (paraId, rangeId) { + await CustomAnnotator.prototype.onAccept.call(this); + await Asc.Editor.callMethod("StartAction", ["GroupActions"]); -/** - * @param {string} paraId - * @param {string} rangeId - * @return {HintInfoForPopup} - */ -AssistantHint.prototype.getInfoForPopup = function(paraId, rangeId) -{ - let _s = this.getAnnotation(paraId, rangeId); - let reason = _s["reason"]; - try { - reason = reason.replace(//gi, ''); - } catch (e) { - console.error(e); - } - return { - original : _s["original"], - explanation : reason, - type : this.type - }; -}; + let range = this.getAnnotationRangeObj(paraId, rangeId); + await Asc.Editor.callMethod("SelectAnnotationRange", [range]); -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantHint.prototype.onAccept = async function(paraId, rangeId) -{ - await CustomAnnotator.prototype.onAccept.call(this); - await Asc.Editor.callMethod("StartAction", ["GroupActions"]); + await Asc.Editor.callCommand(function () { + Api.GetDocument().RemoveSelection(); + }); - let range = this.getAnnotationRangeObj(paraId, rangeId); - await Asc.Editor.callMethod("SelectAnnotationRange", [range]); - - await Asc.Editor.callCommand(function(){ - Api.GetDocument().RemoveSelection(); - }); - - await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); - await Asc.Editor.callMethod("EndAction", ["GroupActions"]); - await Asc.Editor.callMethod("FocusEditor"); -}; + await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); + await Asc.Editor.callMethod("EndAction", ["GroupActions"]); + await Asc.Editor.callMethod("FocusEditor"); + }, +}); diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace-hint.js b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace-hint.js index 065890d3..7aab6440 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace-hint.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace-hint.js @@ -38,99 +38,71 @@ * @constructor * @extends CustomAnnotator */ -function AssistantReplaceHint(annotationPopup, assistantData) -{ - CustomAnnotator.call(this, annotationPopup, assistantData); +function AssistantReplaceHint(annotationPopup, assistantData) { + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantReplaceHint.prototype = Object.create(CustomAnnotator.prototype); AssistantReplaceHint.prototype.constructor = AssistantReplaceHint; -/** - * @param {string} paraId - * @param {string} recalcId - * @param {string} text - */ -AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalcId, text) -{ - this.paragraphs[paraId] = {}; +Object.assign(AssistantReplaceHint.prototype, { + /** + * @param {string} text + * @param {ReplaceHintAiResponse[]} matches + */ + _convertToRanges: function (paraId, text, matches) { + const _t = this; + let rangeId = 1; + const ranges = []; + for (const { + origin, + suggestion, + difference, + reason, + paragraph, + occurrence, + confidence, + } of matches) { + if (origin === suggestion || confidence <= 0.7) continue; - if (text.length === 0) - return false; + let count = 0; + let searchStart = 0; - const argPrompt = this._createPrompt(text); + while (searchStart < text.length) { + const index = _t.simpleGraphemeIndexOf( + text, + origin, + searchStart, + ); + if (index === -1) break; - let response = await this.chatRequest(argPrompt); - if (!response) - return false; - - let rangeId = 1; - let ranges = []; + count++; + if (count === occurrence) { + ranges.push({ + start: index, + length: [...origin].length, + id: rangeId, + }); + _t.paragraphs[paraId][rangeId] = { + original: origin, + suggestion: suggestion, + difference: difference, + reason: reason, + }; + ++rangeId; + break; + } + searchStart = index + 1; + } + } + return ranges; + }, - let _t = this; - - /** - * @param {string} text - * @param {ReplaceHintAiResponse[]} matches - */ - function convertToRanges(text, matches) - { - for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of matches) - { - if (origin === suggestion || confidence <= 0.7) - continue; - - let count = 0; - let searchStart = 0; - - while (searchStart < text.length) - { - const index = _t.simpleGraphemeIndexOf(text, origin, searchStart); - if (index === -1) break; - - count++; - if (count === occurrence) - { - ranges.push({ - "start": index, - "length": [...origin].length, - "id": rangeId - }); - _t.paragraphs[paraId][rangeId] = { - "original" : origin, - "suggestion" : suggestion, - "difference" : difference, - "reason" : reason - }; - ++rangeId; - break; - } - searchStart = index + 1; - } - } - } - - try - { - convertToRanges(text, JSON.parse(response)); - let obj = { - "type": "highlightText", - "paragraphId": paraId, - "name" : "customAssistant_" + this.assistantData.id, - "recalcId": recalcId, - "ranges": ranges - }; - await Asc.Editor.callMethod("AnnotateParagraph", [obj]); - } - catch (e) - { } -} - -/** - * @param {string} text - * @returns {string} - */ -AssistantReplaceHint.prototype._createPrompt = function(text) { - let prompt = `You are a multi-disciplinary text analysis and transformation assistant. + /** + * @param {string} text + * @returns {string} + */ + _createPrompt: function (text) { + let prompt = `You are a multi-disciplinary text analysis and transformation assistant. Your task is to analyze text based on user's specific criteria and provide intelligent corrections. MANDATORY RULES: @@ -180,58 +152,58 @@ AssistantReplaceHint.prototype._createPrompt = function(text) { - DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible - The response should start with [ and end with ] `; - prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - - prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; + prompt += + "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; + prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; - return prompt; -} + prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; -/** - * @param {string} paraId - * @param {string} rangeId - * @returns {ReplaceHintInfoForPopup} - */ -AssistantReplaceHint.prototype.getInfoForPopup = function(paraId, rangeId) -{ - let _s = this.getAnnotation(paraId, rangeId); - let reason = _s["reason"]; - try { - reason = reason.replace(//gi, ''); - } catch (e) { - console.error(e); - } - return { - original : _s["original"], - suggested : _s["difference"], - explanation : reason, - type : this.type - }; -}; + return prompt; + }, -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId) -{ - await CustomAnnotator.prototype.onAccept.call(this, paraId, rangeId); - let text = this.getAnnotation(paraId, rangeId)["suggestion"]; - - await Asc.Editor.callMethod("StartAction", ["GroupActions"]); + /** + * @param {string} paraId + * @param {string} rangeId + * @returns {ReplaceHintInfoForPopup} + */ + getInfoForPopup: function (paraId, rangeId) { + let _s = this.getAnnotation(paraId, rangeId); + let reason = _s["reason"]; + try { + reason = reason.replace(//gi, ''); + } catch (e) { + console.error(e); + } + return { + original: _s["original"], + suggested: _s["difference"], + explanation: reason, + type: this.type, + }; + }, - let range = this.getAnnotationRangeObj(paraId, rangeId); - await Asc.Editor.callMethod("SelectAnnotationRange", [range]); - - Asc.scope.text = text; - await Asc.Editor.callCommand(function(){ - Api.ReplaceTextSmart([Asc.scope.text]); - Api.GetDocument().RemoveSelection(); - }); - - await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); - await Asc.Editor.callMethod("EndAction", ["GroupActions"]); - await Asc.Editor.callMethod("FocusEditor"); -}; + /** + * @param {string} paraId + * @param {string} rangeId + */ + onAccept: async function (paraId, rangeId) { + await CustomAnnotator.prototype.onAccept.call(this, paraId, rangeId); + let text = this.getAnnotation(paraId, rangeId)["suggestion"]; + + await Asc.Editor.callMethod("StartAction", ["GroupActions"]); + + let range = this.getAnnotationRangeObj(paraId, rangeId); + await Asc.Editor.callMethod("SelectAnnotationRange", [range]); + + Asc.scope.text = text; + await Asc.Editor.callCommand(function () { + Api.ReplaceTextSmart([Asc.scope.text]); + Api.GetDocument().RemoveSelection(); + }); + + await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); + await Asc.Editor.callMethod("EndAction", ["GroupActions"]); + await Asc.Editor.callMethod("FocusEditor"); + }, +}); diff --git a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace.js b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace.js index 01bac3c8..a89cecf1 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/custom-annotations/assistant-replace.js @@ -38,97 +38,67 @@ * @constructor * @extends CustomAnnotator */ -function AssistantReplace(annotationPopup, assistantData) -{ - CustomAnnotator.call(this, annotationPopup, assistantData); +function AssistantReplace(annotationPopup, assistantData) { + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantReplace.prototype = Object.create(CustomAnnotator.prototype); AssistantReplace.prototype.constructor = AssistantReplace; -/** - * @param {string} paraId - * @param {string} recalcId - * @param {string} text - */ -AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId, text) -{ - this.paragraphs[paraId] = {}; +Object.assign(AssistantReplace.prototype, { + /** + * @param {string} text + * @param {ReplaceAiResponse[]} matches + */ + _convertToRanges: function (paraId, text, matches) { + const _t = this; + let rangeId = 1; + const ranges = []; + for (const { + origin, + suggestion, + paragraph, + occurrence, + confidence, + } of matches) { + if (origin === suggestion || confidence <= 0.7) continue; - if (text.length === 0) - return false; + let count = 0; + let searchStart = 0; - const argPrompt = this._createPrompt(text); + while (searchStart < text.length) { + const index = _t.simpleGraphemeIndexOf( + text, + origin, + searchStart, + ); + if (index === -1) break; - let response = await this.chatRequest(argPrompt); - if (!response) - return false; + count++; + if (count === occurrence) { + ranges.push({ + start: index, + length: [...origin].length, + id: rangeId, + }); + _t.paragraphs[paraId][rangeId] = { + original: origin, + suggestion: suggestion, + }; + ++rangeId; + break; + } + searchStart = index + 1; + } + } + return ranges; + }, - let rangeId = 1; - let ranges = []; - - let _t = this; - - /** - * @param {string} text - * @param {ReplaceAiResponse[]} matches - */ - function convertToRanges(text, matches) - { - for (const { origin, suggestion, paragraph, occurrence, confidence } of matches) - { - if (origin === suggestion || confidence <= 0.7) - continue; - - let count = 0; - let searchStart = 0; - - while (searchStart < text.length) - { - const index = _t.simpleGraphemeIndexOf(text, origin, searchStart); - if (index === -1) break; - - count++; - if (count === occurrence) - { - ranges.push({ - "start": index, - "length": [...origin].length, - "id": rangeId - }); - _t.paragraphs[paraId][rangeId] = { - "original" : origin, - "suggestion" : suggestion, - }; - ++rangeId; - break; - } - searchStart = index + 1; - } - } - } - - try - { - convertToRanges(text, JSON.parse(response)); - let obj = { - "type": "highlightText", - "paragraphId": paraId, - "name" : "customAssistant_" + this.assistantData.id, - "recalcId": recalcId, - "ranges": ranges - }; - await Asc.Editor.callMethod("AnnotateParagraph", [obj]); - } - catch (e) - { } -} - -/** - * @param {string} text - * @returns {string} - */ -AssistantReplace.prototype._createPrompt = function(text) { - let prompt = `You are a multi-disciplinary text analysis and transformation assistant. + /** + * @param {string} text + * @returns {string} + */ + _createPrompt: function (text) { + let prompt = `You are a multi-disciplinary text analysis and transformation assistant. Your task is to analyze text based on user's specific criteria and provide intelligent corrections. MANDATORY RULES: @@ -174,51 +144,51 @@ AssistantReplace.prototype._createPrompt = function(text) { - DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible - The response should start with [ and end with ] `; - prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - - prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; + prompt += + "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n"; - prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; + prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n"; - return prompt; -} + prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`; -/** - * @param {string} paraId - * @param {string} rangeId - * @returns {ReplaceInfoForPopup} - */ -AssistantReplace.prototype.getInfoForPopup = function(paraId, rangeId) -{ - let _s = this.getAnnotation(paraId, rangeId); - return { - original : _s["original"], - suggested : _s["suggestion"], - type : this.type - }; -}; + return prompt; + }, -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantReplace.prototype.onAccept = async function(paraId, rangeId) -{ - await CustomAnnotator.prototype.onAccept.call(this); - let text = this.getAnnotation(paraId, rangeId)["suggestion"]; - - await Asc.Editor.callMethod("StartAction", ["GroupActions"]); + /** + * @param {string} paraId + * @param {string} rangeId + * @returns {ReplaceInfoForPopup} + */ + getInfoForPopup: function (paraId, rangeId) { + let _s = this.getAnnotation(paraId, rangeId); + return { + original: _s["original"], + suggested: _s["suggestion"], + type: this.type, + }; + }, - let range = this.getAnnotationRangeObj(paraId, rangeId); - await Asc.Editor.callMethod("SelectAnnotationRange", [range]); - - Asc.scope.text = text; - await Asc.Editor.callCommand(function(){ - Api.ReplaceTextSmart([Asc.scope.text]); - Api.GetDocument().RemoveSelection(); - }); - - await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); - await Asc.Editor.callMethod("EndAction", ["GroupActions"]); - await Asc.Editor.callMethod("FocusEditor"); -}; + /** + * @param {string} paraId + * @param {string} rangeId + */ + onAccept: async function (paraId, rangeId) { + await CustomAnnotator.prototype.onAccept.call(this); + let text = this.getAnnotation(paraId, rangeId)["suggestion"]; + + await Asc.Editor.callMethod("StartAction", ["GroupActions"]); + + let range = this.getAnnotationRangeObj(paraId, rangeId); + await Asc.Editor.callMethod("SelectAnnotationRange", [range]); + + Asc.scope.text = text; + await Asc.Editor.callCommand(function () { + Api.ReplaceTextSmart([Asc.scope.text]); + Api.GetDocument().RemoveSelection(); + }); + + await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); + await Asc.Editor.callMethod("EndAction", ["GroupActions"]); + await Asc.Editor.callMethod("FocusEditor"); + }, +}); 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 58db8a1f..50e376e3 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 @@ -37,61 +37,86 @@ * @constructor * @extends TextAnnotator */ -function CustomAnnotator(annotationPopup, assistantData) -{ - TextAnnotator.call(this, annotationPopup); - this.assistantData = assistantData; - this.type = assistantData.type; - this._skipNextChangeParagraph = false; +function CustomAnnotator(annotationPopup, assistantData) { + TextAnnotator.call(this, annotationPopup); + this.assistantData = assistantData; + this.type = assistantData.type; + this._skipNextChangeParagraph = false; } CustomAnnotator.prototype = Object.create(TextAnnotator.prototype); CustomAnnotator.prototype.constructor = CustomAnnotator; -/** - * @param {string} paraId - * @param {string} rangeId - */ -CustomAnnotator.prototype.getAnnotationRangeObj = function(paraId, rangeId) -{ - return { - "paragraphId" : paraId, - "rangeId" : rangeId, - "name" : "customAssistant_" + this.assistantData.id - }; -}; -CustomAnnotator.prototype._handleNewRangePositions = async function(range, paraId, text) -{ - if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId]) - return; +Object.assign(CustomAnnotator.prototype, { + /** + * @param {string} paraId + * @param {string} recalcId + * @param {string} text + */ + annotateParagraph: async function (paraId, recalcId, text) { + this.paragraphs[paraId] = {}; - let rangeId = range["id"]; - let annot = this.getAnnotation(paraId, rangeId); - - if (!annot) - return; - - let start = range["start"]; - let len = range["length"]; + if (text.length === 0) return false; - if (annot["original"] !== text.substring(start, start + len)) - { - let annotRange = this.getAnnotationRangeObj(paraId, rangeId); - Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]); - } -}; -/** - * @param {string[]} paraIds - */ -CustomAnnotator.prototype.checkParagraphs = async function(paraIds) -{ - if (this._skipNextChangeParagraph) - { - this._skipNextChangeParagraph = false; - return; - } - TextAnnotator.prototype.checkParagraphs.call(this, paraIds); -}; -CustomAnnotator.prototype.onAccept = async function(paraId, rangeId) -{ - this._skipNextChangeParagraph = true; -}; + const argPrompt = this._createPrompt(text); + + let response = await this.chatRequest(argPrompt); + if (!response) return false; + + try { + const ranges = this._convertToRanges(paraId, text, JSON.parse(response)); + let obj = { + type: "highlightText", + paragraphId: paraId, + name: "customAssistant_" + this.assistantData.id, + recalcId: recalcId, + ranges: ranges, + }; + await Asc.Editor.callMethod("AnnotateParagraph", [obj]); + } catch (e) {} + }, + /** + * @param {string} paraId + * @param {string} rangeId + */ + getAnnotationRangeObj: function (paraId, rangeId) { + return { + paragraphId: paraId, + rangeId: rangeId, + name: "customAssistant_" + this.assistantData.id, + }; + }, + _handleNewRangePositions: async function (range, paraId, text) { + if ( + !range || + range["name"] !== "customAssistant_" + this.assistantData.id || + !this.paragraphs[paraId] + ) + return; + + let rangeId = range["id"]; + let annot = this.getAnnotation(paraId, rangeId); + + if (!annot) return; + + let start = range["start"]; + let len = range["length"]; + + if (annot["original"] !== text.substring(start, start + len)) { + let annotRange = this.getAnnotationRangeObj(paraId, rangeId); + Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]); + } + }, + /** + * @param {string[]} paraIds + */ + checkParagraphs: async function (paraIds) { + if (this._skipNextChangeParagraph) { + this._skipNextChangeParagraph = false; + return; + } + 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 898b3b85..460338f5 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 @@ -41,14 +41,14 @@ class CustomAssistantManager { constructor() { - /** - * @type {Map} - */ + /** + * @type {Map} + */ this._customAssistants = new Map(); this._isCustomAssistantInit = new Map(); this._isCustomAssistantRunning = new Map(); - /** @type {Map} */ - this._paragraphsStack = new Map(); + /** @type {Map} */ + this._paragraphsStack = new Map(); } /** @@ -62,19 +62,30 @@ class CustomAssistantManager { let assistant = null; switch (assistantData.type) { case 0: - assistant = new AssistantHint(customAnnotationPopup, assistantData); + assistant = new AssistantHint( + customAnnotationPopup, + assistantData, + ); break; case 1: - assistant = new AssistantReplaceHint(customAnnotationPopup, assistantData); + assistant = new AssistantReplaceHint( + customAnnotationPopup, + assistantData, + ); break; case 2: - assistant = new AssistantReplace(customAnnotationPopup, assistantData); + assistant = new AssistantReplace( + customAnnotationPopup, + assistantData, + ); break; } if (!assistant) { - throw new Error("Unknown custom assistant type: " + assistantData.type); + throw new Error( + "Unknown custom assistant type: " + assistantData.type, + ); } - + this._isCustomAssistantInit.set(assistantData.id, false); this._isCustomAssistantRunning.set(assistantData.id, false); this._customAssistants.set(assistantData.id, assistant); @@ -96,13 +107,13 @@ class CustomAssistantManager { if (!isRunning) { return assistant; } - + this._paragraphsStack.forEach((value, paraId) => { assistant.onChangeParagraph( paraId, value.recalcId, value.text, - value.annotations + value.annotations, ); }); const paragraphIdsToUpdate = [...assistant.checked]; @@ -118,14 +129,11 @@ class CustomAssistantManager { this._isCustomAssistantRunning.delete(assistantId); } - /** @param {string} assistantId */ + /** @param {string} assistantId */ checkNeedToRunAssistant(assistantId) { - const isRunning = this._isCustomAssistantRunning.get(assistantId); - this._isCustomAssistantRunning.set( - assistantId, - !isRunning - ); - return isRunning; + const isRunning = this._isCustomAssistantRunning.get(assistantId); + this._isCustomAssistantRunning.set(assistantId, !isRunning); + return isRunning; } /** @@ -134,53 +142,53 @@ class CustomAssistantManager { */ run(assistantId, paraIds) { const assistant = this._customAssistants.get(assistantId); - if (!assistant) { - console.error("Custom assistant not found: " + assistantId); - return; - } + if (!assistant) { + console.error("Custom assistant not found: " + assistantId); + return; + } - if (!this._isCustomAssistantInit.get(assistantId)) { - this._paragraphsStack.forEach((value, paraId) => { - assistant.onChangeParagraph( - paraId, - value.recalcId, - value.text, - value.annotations - ) - }); - } + if (!this._isCustomAssistantInit.get(assistantId)) { + this._paragraphsStack.forEach((value, paraId) => { + assistant.onChangeParagraph( + paraId, + value.recalcId, + value.text, + value.annotations, + ); + }); + } assistant.checkParagraphs(paraIds); - this._isCustomAssistantInit.set(assistantId, true); + this._isCustomAssistantInit.set(assistantId, true); } /** * @param {string} paragraphId - * @param {number} recalcId + * @param {string} recalcId * @param {string} text * @param {*} annotations */ onChangeParagraph(paragraphId, recalcId, text, annotations) { - this._paragraphsStack.set(paragraphId, { - recalcId, - text, - annotations - }); + this._paragraphsStack.set(paragraphId, { + recalcId, + text, + annotations, + }); this._customAssistants.forEach((assistant, assistantId) => { const isInit = this._isCustomAssistantInit.get(assistantId); - if (!isInit) { - return; - } - assistant.onChangeParagraph( + if (!isInit) { + return; + } + assistant.onChangeParagraph( paragraphId, recalcId, text, - annotations + annotations, ); - const isRunning = this._isCustomAssistantRunning.get(assistantId); - if (isRunning) { - assistant.checkParagraphs([paragraphId]); - } + const isRunning = this._isCustomAssistantRunning.get(assistantId); + if (isRunning) { + assistant.checkParagraphs([paragraphId]); + } }); } 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 a5edcc98..3e89196c 100644 --- a/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js +++ b/sdkjs-plugins/content/ai/scripts/text-annotations/text-annotator.js @@ -37,7 +37,7 @@ function TextAnnotator(annotatorPopup) this.rangeId = null; this.paragraphs = {}; - /** @type {Object.} */ + /** @type {Object.} */ this.waitParagraphs = {}; this.paraToCheck = new Set(); /** @type {Set} */ @@ -47,7 +47,7 @@ function TextAnnotator(annotatorPopup) } /** * @param {string} paraId - * @param {number} recalcId + * @param {string} recalcId * @param {string} text * @param {string[]} ranges */