Shows how to leverage the Writing Assistance API to manage annotations and use annotation events.
This sample demonstrates how to manage annotations and use annotation events.
**Important**: You need a Microsoft 365 subscription in order to use Annotation APIs. See GitHub issue 4953 for more information.
let eventContexts = [];
async function registerEventHandlers() {
// Registers event handlers.
await Word.run(async (context) => {
eventContexts[0] = context.document.onParagraphAdded.add(paragraphChanged);
eventContexts[1] = context.document.onParagraphChanged.add(paragraphChanged);
eventContexts[2] = context.document.onAnnotationClicked.add(onClickedHandler);
eventContexts[3] = context.document.onAnnotationHovered.add(onHoveredHandler);
eventContexts[4] = context.document.onAnnotationInserted.add(onInsertedHandler);
eventContexts[5] = context.document.onAnnotationRemoved.add(onRemovedHandler);
eventContexts[6] = context.document.onAnnotationPopupAction.add(onPopupActionHandler);
await context.sync();
console.log("Event handlers registered.");
});
}
async function paragraphChanged(args: Word.ParagraphChangedEventArgs) {
await Word.run(async (context) => {
const results = [];
for (let id of args.uniqueLocalIds) {
let para = context.document.getParagraphByUniqueLocalId(id);
para.load("uniqueLocalId");
results.push({ para: para, text: para.getText() });
}
await context.sync();
for (let result of results) {
console.log(`${args.type}: ID ${result.para.uniqueLocalId}:-`, result.text.value);
}
});
}
async function insertAnnotations() {
// Adds annotations to the selected paragraph.
await Word.run(async (context) => {
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
const options: Word.CritiquePopupOptions = {
brandingTextResourceId: "PG.TabLabel",
subtitleResourceId: "PG.HelpCommand.TipTitle",
titleResourceId: "PG.HelpCommand.Label",
suggestions: ["suggestion 1", "suggestion 2", "suggestion 3"]
};
const critique1: Word.Critique = {
colorScheme: Word.CritiqueColorScheme.red,
start: 1,
length: 3,
popupOptions: options
};
const critique2: Word.Critique = {
colorScheme: Word.CritiqueColorScheme.green,
start: 6,
length: 1,
popupOptions: options
};
const critique3: Word.Critique = {
colorScheme: Word.CritiqueColorScheme.blue,
start: 10,
length: 3,
popupOptions: options
};
const critique4: Word.Critique = {
colorScheme: Word.CritiqueColorScheme.lavender,
start: 14,
length: 3,
popupOptions: options
};
const critique5: Word.Critique = {
colorScheme: Word.CritiqueColorScheme.berry,
start: 18,
length: 10,
popupOptions: options
};
const annotationSet: Word.AnnotationSet = {
critiques: [critique1, critique2, critique3, critique4, critique5]
};
const annotationIds = paragraph.insertAnnotations(annotationSet);
await context.sync();
console.log("Annotations inserted:", annotationIds.value);
});
}
async function getAnnotations() {
// Gets annotations found in the selected paragraph.
await Word.run(async (context) => {
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
annotations.load("id,state,critiqueAnnotation");
await context.sync();
console.log("Annotations found:");
for (let i = 0; i < annotations.items.length; i++) {
const annotation: Word.Annotation = annotations.items[i];
console.log(`ID ${annotation.id} - state '${annotation.state}':`, annotation.critiqueAnnotation.critique);
}
});
}
async function acceptFirst() {
// Accepts the first annotation found in the selected paragraph.
await Word.run(async (context) => {
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
annotations.load("id,state,critiqueAnnotation");
await context.sync();
for (let i = 0; i < annotations.items.length; i++) {
const annotation: Word.Annotation = annotations.items[i];
if (annotation.state === Word.AnnotationState.created) {
console.log(`Accepting ID ${annotation.id}...`);
annotation.critiqueAnnotation.accept();
await context.sync();
break;
}
}
});
}
async function rejectLast() {
// Rejects the last annotation found in the selected paragraph.
await Word.run(async (context) => {
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
annotations.load("id,state,critiqueAnnotation");
await context.sync();
for (let i = annotations.items.length - 1; i >= 0; i--) {
const annotation: Word.Annotation = annotations.items[i];
if (annotation.state === Word.AnnotationState.created) {
console.log(`Rejecting ID ${annotation.id}...`);
annotation.critiqueAnnotation.reject();
await context.sync();
break;
}
}
});
}
async function deleteAnnotations() {
// Deletes all annotations found in the selected paragraph.
await Word.run(async (context) => {
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
annotations.load("id");
await context.sync();
const ids = [];
for (let i = 0; i < annotations.items.length; i++) {
const annotation: Word.Annotation = annotations.items[i];
ids.push(annotation.id);
annotation.delete();
}
await context.sync();
console.log("Annotations deleted:", ids);
});
}
async function onClickedHandler(args: Word.AnnotationClickedEventArgs) {
await Word.run(async (context) => {
const annotation: Word.Annotation = context.document.getAnnotationById(args.id);
annotation.load("critiqueAnnotation");
await context.sync();
console.log(`AnnotationClicked: ID ${args.id}:`, annotation.critiqueAnnotation.critique);
});
}
async function onHoveredHandler(args: Word.AnnotationHoveredEventArgs) {
await Word.run(async (context) => {
const annotation: Word.Annotation = context.document.getAnnotationById(args.id);
annotation.load("critiqueAnnotation");
await context.sync();
console.log(`AnnotationHovered: ID ${args.id}:`, annotation.critiqueAnnotation.critique);
});
}
async function onInsertedHandler(args: Word.AnnotationInsertedEventArgs) {
await Word.run(async (context) => {
const annotations = [];
for (let i = 0; i < args.ids.length; i++) {
let annotation: Word.Annotation = context.document.getAnnotationById(args.ids[i]);
annotation.load("id,critiqueAnnotation");
annotations.push(annotation);
}
await context.sync();
for (let annotation of annotations) {
console.log(`AnnotationInserted: ID ${annotation.id}:`, annotation.critiqueAnnotation.critique);
}
});
}
async function onRemovedHandler(args: Word.AnnotationRemovedEventArgs) {
await Word.run(async (context) => {
for (let id of args.ids) {
console.log(`AnnotationRemoved: ID ${id}`);
}
});
}
async function onPopupActionHandler(args: Word.AnnotationPopupActionEventArgs) {
await Word.run(async (context) => {
let message = `AnnotationPopupAction: ID ${args.id} = `;
if (args.action === "Accept") {
message += `Accepted: ${args.critiqueSuggestion}`;
} else {
message += "Rejected";
}
console.log(message);
});
}
async function deregisterEventHandlers() {
// Deregisters event handlers.
await Word.run(async (context) => {
for (let i = 0; i < eventContexts.length; i++) {
await Word.run(eventContexts[i].context, async (context) => {
eventContexts[i].remove();
});
}
await context.sync();
eventContexts = [];
console.log("Removed event handlers.");
});
}
async function setup() {
await Word.run(async (context) => {
const body: Word.Body = context.document.body;
body.clear();
body.insertParagraph(
"Do you want to create a solution that extends the functionality of Word? You can use the Office Add-ins platform to extend Word clients running on the web, on a Windows desktop, or on a Mac.",
"Start"
);
body.insertParagraph(
"Use add-in commands to extend the Word UI and launch task panes that run JavaScript that interacts with the content in a Word document. Any code that you can run in a browser can run in a Word add-in. Add-ins that interact with content in a Word document create requests to act on Word objects and synchronize object state.",
"End"
);
});
}
// Default helper for invoking an action and handling errors.
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
console.error(error);
}
}