diff --git a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-hint.js b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-hint.js index 1391e2d7..48b8f48c 100644 --- a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-hint.js +++ b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-hint.js @@ -34,9 +34,9 @@ /// /** @param {localStorageCustomAssistantItem} assistantData */ -function AssistantHint(assistantData) +function AssistantHint(annotationPopup, assistantData) { - CustomAnnotator.call(this, assistantData); + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantHint.prototype = Object.create(CustomAnnotator.prototype); AssistantHint.prototype.constructor = AssistantHint; @@ -58,7 +58,7 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex let response = await this.chatRequest(argPrompt); if (!response) return false; - + let rangeId = 1; let ranges = []; @@ -216,36 +216,3 @@ AssistantHint.prototype.onAccept = async function(paraId, rangeId) await Asc.Editor.callMethod("EndAction", ["GroupActions"]); await Asc.Editor.callMethod("FocusEditor"); }; - -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantHint.prototype.getAnnotationRangeObj = function(paraId, rangeId) -{ - return { - "paragraphId" : paraId, - "rangeId" : rangeId, - "name" : "customAssistant_" + this.assistantData.id - }; -}; -AssistantHint.prototype._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]); - } -}; diff --git a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace-hint.js b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace-hint.js index 9f4aa1c2..2bb62250 100644 --- a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace-hint.js +++ b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace-hint.js @@ -34,9 +34,9 @@ /// /** @param {localStorageCustomAssistantItem} assistantData */ -function AssistantReplaceHint(assistantData) +function AssistantReplaceHint(annotationPopup, assistantData) { - CustomAnnotator.call(this, assistantData); + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantReplaceHint.prototype = Object.create(CustomAnnotator.prototype); AssistantReplaceHint.prototype.constructor = AssistantReplaceHint; @@ -52,11 +52,12 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc if (text.length === 0) return false; - + const argPrompt = this._createPrompt(text); + let response = await this.chatRequest(argPrompt); if (!response) - return false; + return false; let rangeId = 1; let ranges = []; @@ -65,11 +66,11 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc /** * @param {string} text - * @param {ReplaceHintAiResponse[]} corrections + * @param {ReplaceHintAiResponse[]} matches */ - function convertToRanges(text, corrections) + function convertToRanges(text, matches) { - for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of corrections) + for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of matches) { if (origin === suggestion || confidence <= 0.7) continue; @@ -229,36 +230,3 @@ AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId) await Asc.Editor.callMethod("EndAction", ["GroupActions"]); await Asc.Editor.callMethod("FocusEditor"); }; - -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantReplaceHint.prototype.getAnnotationRangeObj = function(paraId, rangeId) -{ - return { - "paragraphId" : paraId, - "rangeId" : rangeId, - "name" : "customAssistant_" + this.assistantData.id - }; -}; -AssistantReplaceHint.prototype._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]); - } -}; diff --git a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace.js b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace.js index d47da0aa..7718d8ac 100644 --- a/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace.js +++ b/sdkjs-plugins/content/ai/scripts/custom-annotations/assistant-replace.js @@ -34,9 +34,9 @@ /// /** @param {localStorageCustomAssistantItem} assistantData */ -function AssistantReplace(assistantData) +function AssistantReplace(annotationPopup, assistantData) { - CustomAnnotator.call(this, assistantData); + CustomAnnotator.call(this, annotationPopup, assistantData); } AssistantReplace.prototype = Object.create(CustomAnnotator.prototype); AssistantReplace.prototype.constructor = AssistantReplace; @@ -52,12 +52,13 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId, if (text.length === 0) return false; - + const argPrompt = this._createPrompt(text); + let response = await this.chatRequest(argPrompt); if (!response) return false; - + let rangeId = 1; let ranges = []; @@ -65,11 +66,11 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId, /** * @param {string} text - * @param {ReplaceAiResponse[]} corrections + * @param {ReplaceAiResponse[]} matches */ - function convertToRanges(text, corrections) + function convertToRanges(text, matches) { - for (const { origin, suggestion, paragraph, occurrence, confidence } of corrections) + for (const { origin, suggestion, paragraph, occurrence, confidence } of matches) { if (origin === suggestion || confidence <= 0.7) continue; @@ -216,36 +217,3 @@ AssistantReplace.prototype.onAccept = async function(paraId, rangeId) await Asc.Editor.callMethod("EndAction", ["GroupActions"]); await Asc.Editor.callMethod("FocusEditor"); }; - -/** - * @param {string} paraId - * @param {string} rangeId - */ -AssistantReplace.prototype.getAnnotationRangeObj = function(paraId, rangeId) -{ - return { - "paragraphId" : paraId, - "rangeId" : rangeId, - "name" : "customAssistant_" + this.assistantData.id - }; -}; -AssistantReplace.prototype._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]); - } -}; diff --git a/sdkjs-plugins/content/ai/scripts/custom-annotations/custom-annotator.js b/sdkjs-plugins/content/ai/scripts/custom-annotations/custom-annotator.js index f8509aae..8bbf0d97 100644 --- a/sdkjs-plugins/content/ai/scripts/custom-annotations/custom-annotator.js +++ b/sdkjs-plugins/content/ai/scripts/custom-annotations/custom-annotator.js @@ -32,210 +32,44 @@ /// -/** @param {localStorageCustomAssistantItem} assistantData */ -function CustomAnnotator(assistantData) +function CustomAnnotator(annotationPopup, assistantData) { - this.paragraphs = {}; - /** @type {Object.} */ - this.waitParagraphs = {}; - this.paraToCheck = new Set(); - this.checked = new Set(); // was checked on the previous request - - this.type = assistantData.type; // 2 - this.assistantData = assistantData; + TextAnnotator.call(this, annotationPopup); + this.assistantData = assistantData; + this.type = assistantData.type; } +CustomAnnotator.prototype = Object.create(TextAnnotator.prototype); +CustomAnnotator.prototype.constructor = CustomAnnotator; + /** * @param {string} paraId - * @param {number} recalcId - * @param {string} text - * @param {string[]} ranges + * @param {string} rangeId */ -CustomAnnotator.prototype.onChangeParagraph = async function(paraId, recalcId, text, ranges) -{ - this._handleNewRanges(ranges, paraId, text); - this.waitParagraphs[paraId] = { - recalcId : recalcId, - text : text - }; - - this._checkParagraph(paraId); -}; -/** - * @param {string} paraId - * @param {string[]} ranges - */ -CustomAnnotator.prototype.onClick = function(paraId, ranges) -{ - if (!ranges || !ranges.length) - this._closePopup(); - else - this._openPopup(paraId, ranges[0]); -}; -CustomAnnotator.prototype.onBlur = function() -{ - this._closePopup(); -}; -/** - * @param {string[]} paraIds - */ -CustomAnnotator.prototype.checkParagraphs = async function(paraIds) -{ - this.paraToCheck.clear() - paraIds.forEach(function(paraId) { - if (!this.checked.has(paraId) || this.waitParagraphs[paraId]) { - this.paraToCheck.add(paraId); - this._checkParagraph(paraId); - } - }, this); -}; -CustomAnnotator.prototype._checkParagraph = async function(paraId) -{ - if (!this.paraToCheck.has(paraId) || !this.waitParagraphs[paraId]) { - return; - } - - let recalcId = this.waitParagraphs[paraId].recalcId; - let text = this.waitParagraphs[paraId].text; - - // TODO: Temporarily for simplicity - let range = this.getAnnotationRangeObj(paraId); - range["rangeId"] = undefined; - range["all"] = true; - await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); - await this.annotateParagraph(paraId, recalcId, text); - - delete this.waitParagraphs[paraId]; - this.paraToCheck.delete(paraId); - - this.checked.add(paraId); -}; -CustomAnnotator.prototype.annotateParagraph = async function(paraId, recalcId, text) -{ -}; -CustomAnnotator.prototype._openPopup = async function(paraId, rangeId) -{ - if (!customAnnotationPopup) - return; - - /** @type {InfoForPopup} */ - const popupInfo = this.getInfoForPopup(paraId, rangeId); - - let popup = customAnnotationPopup.open(this.type, paraId, rangeId, popupInfo); - if (!popup) - return; - - let _t = this; - popup.onAccept = async function() { - await _t.onAccept(paraId, rangeId); - _t._closePopup(); - }; - popup.onReject = async function() { - await _t.onReject(paraId, rangeId); - _t._closePopup(); - }; -}; -CustomAnnotator.prototype._closePopup = function() -{ - if (!customAnnotationPopup) - return; - - customAnnotationPopup.close(this.type); -}; -CustomAnnotator.prototype.getInfoForPopup = function(paraId, rangeId) -{ - return {}; -}; -CustomAnnotator.prototype.onAccept = async function(paraId, rangeId) -{ -}; -CustomAnnotator.prototype.onReject = async function(paraId, rangeId) -{ - let range = this.getAnnotationRangeObj(paraId, rangeId); - await Asc.Editor.callMethod("RemoveAnnotationRange", [range]); -}; -CustomAnnotator.prototype.getAnnotation = function(paraId, rangeId) -{ - if (!paraId || !rangeId || !this.paragraphs[paraId] || !this.paragraphs[paraId][rangeId]) - return {}; - - return this.paragraphs[paraId][rangeId]; -}; CustomAnnotator.prototype.getAnnotationRangeObj = function(paraId, rangeId) { return { "paragraphId" : paraId, - "rangeId" : rangeId + "rangeId" : rangeId, + "name" : "customAssistant_" + this.assistantData.id }; }; -CustomAnnotator.prototype._handleNewRanges = function(ranges, paraId, text) +CustomAnnotator.prototype._handleNewRangePositions = async function(range, paraId, text) { - if (!ranges || !Array.isArray(ranges)) + if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId]) return; - for (let i = 0; i < ranges.length; ++i) - { - this._handleNewRangePositions(ranges[i], paraId, text); - } -}; -CustomAnnotator.prototype._handleNewRangePositions = function(range, paraId, text) -{ -}; -CustomAnnotator.prototype.chatRequest = async function(prompt) -{ - let requestEngine = AI.Request.create(AI.ActionType.Chat); - if (!requestEngine) - return null; + let rangeId = range["id"]; + let annot = this.getAnnotation(paraId, rangeId); - let response = await requestEngine.chatRequest(prompt, false); - return this.normalizeResponse(response); -}; -/** - * Normalizes AI response by removing markdown code block wrappers - * @param {string} response - The raw AI response that might be wrapped in ```json``` blocks - * @returns {string} - The normalized response with markdown code blocks removed - */ -CustomAnnotator.prototype.normalizeResponse = function(response) { - if (typeof response !== 'string') { - return response; - } + if (!annot) + return; + + let start = range["start"]; + let len = range["length"]; - // Trim whitespace - let normalized = response.trim(); - - // Check if response is wrapped in markdown code blocks - // Patterns: ```json\n{...}\n``` or ```\n{...}\n``` - const codeBlockPattern = /^```(?:json)?\s*\n?([\s\S]*?)\n?```$/; - const match = normalized.match(codeBlockPattern); - - if (match) { - // Extract content between code block markers - normalized = match[1].trim(); + if (annot["original"] !== text.substring(start, start + len)) + { + let annotRange = this.getAnnotationRangeObj(paraId, rangeId); + Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]); } - - return normalized; -}; -/** - * @param {string} str - * @param {string} searchStr - * @param {string} [fromIndex] - * @returns {number} - */ -CustomAnnotator.prototype.simpleGraphemeIndexOf = function(str, searchStr, fromIndex = 0) { - const codeUnitIndex = str.indexOf(searchStr, fromIndex); - if (codeUnitIndex < 2) { - return codeUnitIndex; - } - const adjustedIndex = adjustIndexForSurrogates(str, codeUnitIndex); - - function adjustIndexForSurrogates(str, codeUnitIndex) { - let surrogateCount = 0; - for (let i = 0; i < codeUnitIndex; i++) { - const code = str.charCodeAt(i); - if (code >= 0xD800 && code <= 0xDBFF) { - surrogateCount++; - } - } - return codeUnitIndex - surrogateCount; - } - return adjustedIndex; -} +}; \ No newline at end of file diff --git a/sdkjs-plugins/content/ai/scripts/custom-annotations/manager.js b/sdkjs-plugins/content/ai/scripts/custom-annotations/manager.js index 2711ee1f..15cd9711 100644 --- a/sdkjs-plugins/content/ai/scripts/custom-annotations/manager.js +++ b/sdkjs-plugins/content/ai/scripts/custom-annotations/manager.js @@ -36,11 +36,13 @@ /// /// /// +/// +/// class CustomAssistantManager { constructor() { /** - * @type {Map} + * @type {Map} */ this._customAssistants = new Map(); this._isCustomAssistantInit = new Map(); @@ -62,13 +64,13 @@ class CustomAssistantManager { } switch (assistantData.type) { case 0: - assistant = new AssistantHint(assistantData); + assistant = new AssistantHint(customAnnotationPopup, assistantData); break; case 1: - assistant = new AssistantReplaceHint(assistantData); + assistant = new AssistantReplaceHint(customAnnotationPopup, assistantData); break; case 2: - assistant = new AssistantReplace(assistantData); + assistant = new AssistantReplace(customAnnotationPopup, assistantData); break; default: throw new Error(