refactoring

This commit is contained in:
Artur
2026-01-23 17:24:46 +03:00
parent 0840d14d4c
commit 26a3687951
6 changed files with 471 additions and 526 deletions

View File

@ -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<HintAiResponse>} 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<HintAiResponse>} 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(/<a\s+(.*?)>/gi, '<a $1 target="_blank">');
} 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(/<a\s+(.*?)>/gi, '<a $1 target="_blank">');
} 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");
},
});

View File

@ -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(/<a\s+(.*?)>/gi, '<a $1 target="_blank">');
} 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(/<a\s+(.*?)>/gi, '<a $1 target="_blank">');
} 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");
},
});

View File

@ -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");
},
});

View File

@ -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;
},
});

View File

@ -41,14 +41,14 @@
class CustomAssistantManager {
constructor() {
/**
* @type {Map<string, Assistant>}
*/
/**
* @type {Map<string, Assistant>}
*/
this._customAssistants = new Map();
this._isCustomAssistantInit = new Map();
this._isCustomAssistantRunning = new Map();
/** @type {Map<string, {recalcId: number, text: string, annotations: any}>} */
this._paragraphsStack = new Map();
/** @type {Map<string, {recalcId: string, text: string, annotations: any}>} */
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]);
}
});
}

View File

@ -37,7 +37,7 @@ function TextAnnotator(annotatorPopup)
this.rangeId = null;
this.paragraphs = {};
/** @type {Object.<string, {recalcId: number, text: string}>} */
/** @type {Object.<string, {recalcId: string, text: string}>} */
this.waitParagraphs = {};
this.paraToCheck = new Set();
/** @type {Set<string>} */
@ -47,7 +47,7 @@ function TextAnnotator(annotatorPopup)
}
/**
* @param {string} paraId
* @param {number} recalcId
* @param {string} recalcId
* @param {string} text
* @param {string[]} ranges
*/