@veltdev/tiptap-velt-comments
Version:
Tiptap Extension to add Google Docs-style overlay comments to your Tiptap editor. Works with the Velt Collaboration SDK.
345 lines (344 loc) • 14.1 kB
TypeScript
import type { Editor } from '@tiptap/core';
import { Plugin } from "./plugin";
import type { AnnotationData } from './types';
/**
* Interface for tracking annotation data during processing
*/
interface AnnotationToCheck {
id: string;
multiThreadAnnotationId?: string;
annotation: AnnotationData;
nodes: {
node: {
text?: string;
nodeSize?: number;
};
pos: number;
}[];
originalText: string;
originalOccurrence: number;
targetTextNodeId: string;
}
/**
* Interface for text match result
*/
interface TextMatch {
from: number;
to: number;
}
/**
* Interface for tracking annotation changes during a transaction
*/
export interface AnnotationChange {
annotationId: string;
multiThreadAnnotationId?: string;
originalText: string;
currentText: string;
originalOccurrence: number;
currentOccurrence: number;
originalTargetTextNodeId: string;
newTargetTextNodeId: string;
annotation: AnnotationData;
contentChanged: boolean;
occurrenceChanged: boolean;
targetTextNodeIdChanged: boolean;
}
export declare class EditorService {
plugin: Plugin;
constructor(plugin: Plugin);
static getEditorId: (editor: Editor) => string | null;
getSelectedText: (editor: Editor) => string | null;
findParentNodeWithId(editor: Editor): HTMLElement | null;
onTransaction({ editor, name }: {
editor: Editor;
name: string;
}): void;
/**
* Collects all annotations present in the document for processing.
* This function traverses the document tree and collects all text nodes that have
* the specified mark type, organizing them by their annotation IDs.
*
* @param editor - The Tiptap editor instance to traverse
* @param markType - The type of mark to look for (e.g., 'velt-comment')
* @param annotationStore - An object with a get method to retrieve annotation data by ID
*
* @returns A Map where:
* - Key: annotation ID (string)
* - Value: AnnotationToCheck object containing:
* - id: The annotation ID
* - multiThreadAnnotationId: Optional ID for multi-thread annotations
* - annotation: The full annotation data from the store
* - nodes: Array of text nodes with their positions and content
* - originalText: The original text content of the annotation
* - originalOccurrence: The original occurrence number
* - targetTextNodeId: The ID of the target text node
*
* @example
* ```typescript
* const annotations = collectDocumentAnnotations(editor, 'velt-comment', annotationStore);
* // Returns: Map<string, AnnotationToCheck>
* ```
*
* @remarks
* - Only processes text nodes that have the specified mark type
* - Skips nodes without valid annotation IDs
* - Groups multiple occurrences of the same annotation together
* - Preserves original text and occurrence information for change detection
*/
collectDocumentAnnotations(editor: Editor, markType: string): Map<string, AnnotationToCheck>;
/**
* Detects content changes in an annotation by comparing current and original text.
* This function is used by the TiptapVeltComments extension to track text modifications
* within annotated regions.
*
* @param nodes - Array of text nodes from the annotation
* @param originalText - The original text content of the annotation
* @returns Object containing:
* - currentText: The current text content after joining all nodes
* - contentChanged: Boolean indicating if text content has changed
*
* @example
* ```typescript
* const { currentText, contentChanged } = detectContentChanges(nodes, "Original text");
* if (contentChanged) {
* console.log(`Text changed from "Original text" to "${currentText}"`);
* }
* ```
*/
detectContentChanges(nodes: AnnotationToCheck['nodes'], originalText: string): {
currentText: string;
contentChanged: boolean;
};
/**
* Detects container changes and updates search results accordingly.
* This function is part of the TiptapVeltComments extension's change detection system,
* responsible for tracking when annotations move between different DOM containers.
*
* @param editor - The Tiptap editor instance
* @param params - Object containing:
* - currentText: The current text content to search for
* - targetTextNodeId: The ID of the current container node
* - nodes: Array of text nodes from the annotation
* @returns Object containing:
* - targetTextNodeIdChanged: Boolean indicating if container has changed
* - newTargetTextNodeId: The ID of the new container (if changed)
* - searchResults: Array of text matches found in the container
*
* @example
* ```typescript
* const result = detectContainerChanges(editor, {
* currentText: "Example text",
* targetTextNodeId: "container-1",
* nodes: [...]
* });
* if (result.targetTextNodeIdChanged) {
* console.log(`Annotation moved to container: ${result.newTargetTextNodeId}`);
* }
* ```
*/
detectContainerChanges(editor: Editor, params: {
annotationId?: string;
currentText: string;
targetTextNodeId: string;
nodes: AnnotationToCheck['nodes'];
}): {
targetTextNodeIdChanged: boolean;
newTargetTextNodeId: string;
searchResults: TextMatch[];
};
/**
* Creates an AnnotationChange object from the detected changes.
* This function is used by the TiptapVeltComments extension to format
* change information for the Velt comment system.
*
* @param data - The original annotation data
* @param changes - Object containing all detected changes:
* - currentText: The current text content
* - contentChanged: Boolean indicating text change
* - currentOccurrence: The new occurrence number
* - occurrenceChanged: Boolean indicating occurrence change
* - targetTextNodeIdChanged: Boolean indicating container change
* - newTargetTextNodeId: The new container ID
* @returns A complete AnnotationChange object ready for Velt processing
*
* @example
* ```typescript
* const change = createAnnotationChange(annotationData, {
* currentText: "Updated text",
* contentChanged: true,
* currentOccurrence: 2,
* occurrenceChanged: true,
* targetTextNodeIdChanged: false,
* newTargetTextNodeId: "container-1"
* });
* ```
*/
createAnnotationChange(data: AnnotationToCheck, changes: {
currentText: string;
contentChanged: boolean;
currentOccurrence: number;
occurrenceChanged: boolean;
targetTextNodeIdChanged: boolean;
newTargetTextNodeId: string;
}): AnnotationChange;
/**
* Processes a single annotation to detect changes in content, occurrence, or container.
* This function is a critical part of the TiptapVeltComments extension, responsible for
* detecting and tracking changes in annotated text within the editor.
*
* The function works in conjunction with the TiptapVeltComments extension to:
* 1. Track text modifications within annotations
* 2. Detect when annotations move between different containers
* 3. Update occurrence numbers when text is duplicated or moved
* 4. Maintain synchronization between Tiptap and Velt's comment system
*
* @param editor - The Tiptap editor instance
* @param data - The annotation data to process
* @returns An AnnotationChange object if changes are detected, null otherwise
*
* @throws {Error} When invalid input data is provided
* @throws {Error} When editor operations fail
*
* @example
* ```typescript
* // Inside TiptapVeltComments extension
* const changes = processAnnotationChanges(editor, annotationData);
* if (changes) {
* // Update Velt comment system with changes
* updateVeltComments(commentElement, new Map([[changes.annotationId, changes]]));
* }
* ```
*
* @remarks
* - This function is called during editor transactions
* - Changes are detected by comparing current state with original state
* - Three types of changes are tracked: content, occurrence, and container
* - The function is part of the TiptapVeltComments extension's change detection system
*/
processAnnotationChanges(editor: Editor, data: AnnotationToCheck): AnnotationChange | null;
processContainerChanges(editor: Editor, params: {
annotationId?: string;
currentText: string;
targetTextNodeId: string;
nodes: {
node: {
text?: string;
nodeSize?: number;
};
pos: number;
}[];
}): {
changed: boolean;
newId: string;
searchResults: TextMatch[];
} | null;
/**
* Calculates the new occurrence number for an annotation based on its position in the document.
* This function is used to determine which instance of a repeated text should be highlighted
* when an annotation spans multiple occurrences of the same text.
*
* @param editor - The Tiptap editor instance
* @param params - Object containing:
* - searchResults: Array of text matches found in the document
* - nodes: Array of text nodes from the annotation with their positions
* - contentChanged: Boolean indicating if the text content has changed
* - currentText: The current text content to search for
* - targetTextNodeId: The ID of the container node (if any)
* - originalOccurrence: The original occurrence number before changes
* @returns The new occurrence number (1-based index) or the original occurrence if no match found
*
* @example
* ```typescript
* const newOccurrence = calculateNewOccurrence(editor, {
* searchResults: [{ from: 0, to: 10 }, { from: 20, to: 30 }],
* nodes: [{ node: { text: "example" }, pos: 25 }],
* contentChanged: false,
* currentText: "example",
* targetTextNodeId: "container-1",
* originalOccurrence: 1
* });
* // Returns: 2 (second occurrence)
* ```
*
* @remarks
* - The function uses a 1-based index for occurrence numbers
* - If no matches are found, returns the original occurrence number
* - Handles cases where annotation spans multiple text nodes
* - Considers both the start and end positions of the annotation
*/
calculateNewOccurrence(editor: Editor, params: {
searchResults: TextMatch[];
nodes: {
node: {
text?: string;
nodeSize?: number;
};
pos: number;
}[];
contentChanged: boolean;
currentText: string;
targetTextNodeId: string | null;
originalOccurrence: number;
}): number;
/**
* Updates the Velt comment element with new annotation data
*/
updateVeltComments(commentElement: {
updateContext: (id: string, context: unknown) => void;
}, changes: Map<string, AnnotationChange>, editor: Editor): void;
/**
* Implements KMP (Knuth-Morris-Pratt) pattern matching algorithm
* @param pattern The pattern to search for
* @returns The longest proper prefix array
*/
computeKMPTable(pattern: string): number[];
/**
* Finds all occurrences of a pattern in text using KMP algorithm with early termination
* @param text The text to search in
* @param pattern The pattern to search for
* @param startPos The starting position offset
* @param maxOccurrences Maximum number of occurrences to find (optional)
* @returns Array of match positions
*/
kmpSearch(text: string, pattern: string, startPos?: number, maxOccurrences?: number): number[];
/**
* Find text within a specific DOM element by its ID using KMP algorithm with early termination
*/
findTextInDomElement(editor: Editor, text: string, domElementId: string, desiredOccurrence?: number): TextMatch[];
/**
* Helper function to find text in the document using KMP with early termination
*/
findTextInDocument(editor: Editor, text: string, desiredOccurrence?: number): TextMatch[];
/**
* Find the occurrence index of selected text
* @param editor The Tiptap editor instance
* @param selectedText The text to find occurrences for
* @param targetTextNode Target text node for scoped searching (optional)
* @param selectionFrom The from position of current selection
* @param selectionTo The to position of current selection
* @returns The 1-indexed occurrence number
*/
findOccurrenceIndex(editor: Editor, selectedText: string, targetTextNode: HTMLElement | null, selectionFrom: number, selectionTo: number): number;
/**
* Highlights text in the editor with a Velt comment
* @param editor The editor instance
* @param textToFind The text to highlight
* @param commentOptions Options for the comment
*/
highlightTextWithVeltComment(editor: Editor, textToFind: string, commentOptions: {
annotationId?: string;
multiThreadAnnotationId?: string;
occurrence?: number;
targetTextNodeId?: string;
originalAnnotation?: any;
}): void;
/**
* Removes a Velt comment mark from the document
* @param editor - The Tiptap editor instance
* @param annotationId - The ID of the annotation to remove
* @returns boolean - True if the annotation was successfully removed, false otherwise
* @throws Error if the stored data is invalid or if the mark type is not found
*/
removeVeltCommentFromEditor(editor: Editor, annotationId: string): boolean;
}
export {};