UNPKG

@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
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 {};