@veltdev/lexical-velt-comments 4.5.2-beta.2 → 4.5.2-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/cjs/index.js +1 -1
  2. package/cjs/index.js.map +1 -1
  3. package/cjs/types/src/adapters/host/doc.d.ts +97 -0
  4. package/cjs/types/src/adapters/host/marks.d.ts +89 -0
  5. package/cjs/types/src/adapters/host/storage.d.ts +37 -0
  6. package/cjs/types/src/adapters/velt.d.ts +55 -0
  7. package/cjs/types/src/constants/common.d.ts +67 -0
  8. package/cjs/types/src/core/extension.d.ts +40 -0
  9. package/cjs/types/src/core/registry.d.ts +71 -0
  10. package/cjs/types/src/core/state.d.ts +86 -0
  11. package/cjs/types/src/features/addComment.d.ts +63 -0
  12. package/cjs/types/src/features/commentRenderer.d.ts +46 -0
  13. package/cjs/types/src/features/renderComments.d.ts +73 -0
  14. package/cjs/types/src/features/updateContent.d.ts +32 -0
  15. package/cjs/types/src/index.d.ts +26 -0
  16. package/cjs/types/src/types/common.d.ts +72 -0
  17. package/cjs/types/src/types/host.d.ts +65 -0
  18. package/cjs/types/src/types/state.d.ts +28 -0
  19. package/cjs/types/src/types/velt.d.ts +6 -0
  20. package/cjs/types/src/utils/common.d.ts +56 -0
  21. package/cjs/types/src/utils/console.d.ts +37 -0
  22. package/cjs/types/src/utils/host.d.ts +44 -0
  23. package/cjs/types/src/utils/serializer.d.ts +19 -0
  24. package/esm/index.js +1 -1
  25. package/esm/index.js.map +1 -1
  26. package/esm/types/src/adapters/host/doc.d.ts +97 -0
  27. package/esm/types/src/adapters/host/marks.d.ts +89 -0
  28. package/esm/types/src/adapters/host/storage.d.ts +37 -0
  29. package/esm/types/src/adapters/velt.d.ts +55 -0
  30. package/esm/types/src/constants/common.d.ts +67 -0
  31. package/esm/types/src/core/extension.d.ts +40 -0
  32. package/esm/types/src/core/registry.d.ts +71 -0
  33. package/esm/types/src/core/state.d.ts +86 -0
  34. package/esm/types/src/features/addComment.d.ts +63 -0
  35. package/esm/types/src/features/commentRenderer.d.ts +46 -0
  36. package/esm/types/src/features/renderComments.d.ts +73 -0
  37. package/esm/types/src/features/updateContent.d.ts +32 -0
  38. package/esm/types/src/index.d.ts +26 -0
  39. package/esm/types/src/types/common.d.ts +72 -0
  40. package/esm/types/src/types/host.d.ts +65 -0
  41. package/esm/types/src/types/state.d.ts +28 -0
  42. package/esm/types/src/types/velt.d.ts +6 -0
  43. package/esm/types/src/utils/common.d.ts +56 -0
  44. package/esm/types/src/utils/console.d.ts +37 -0
  45. package/esm/types/src/utils/host.d.ts +44 -0
  46. package/esm/types/src/utils/serializer.d.ts +19 -0
  47. package/index.d.ts +275 -26
  48. package/package.json +1 -1
  49. package/tsconfig.tsbuildinfo +1 -0
  50. package/cjs/types/comment-node.d.ts +0 -23
  51. package/cjs/types/constants.d.ts +0 -6
  52. package/cjs/types/editor.d.ts +0 -166
  53. package/cjs/types/index.d.ts +0 -24
  54. package/cjs/types/plugin.d.ts +0 -14
  55. package/cjs/types/serializer.d.ts +0 -5
  56. package/cjs/types/store.d.ts +0 -39
  57. package/cjs/types/types.d.ts +0 -40
  58. package/cjs/types/utility.d.ts +0 -122
  59. package/cjs/types/velt.d.ts +0 -21
  60. package/esm/types/comment-node.d.ts +0 -23
  61. package/esm/types/constants.d.ts +0 -6
  62. package/esm/types/editor.d.ts +0 -166
  63. package/esm/types/index.d.ts +0 -24
  64. package/esm/types/plugin.d.ts +0 -14
  65. package/esm/types/serializer.d.ts +0 -5
  66. package/esm/types/store.d.ts +0 -39
  67. package/esm/types/types.d.ts +0 -40
  68. package/esm/types/utility.d.ts +0 -122
  69. package/esm/types/velt.d.ts +0 -21
package/cjs/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/constants.ts","../../src/comment-node.ts","../../src/velt.ts","../../src/utility.ts","../../src/editor.ts","../../src/store.ts","../../src/plugin.ts","../../src/serializer.ts","../../src/index.ts"],"sourcesContent":["export const Constants = {\n ATTRIBUTES: {\n EDITOR_ID: 'data-editor-id',\n LOCATION_ID: 'data-velt-location-id',\n },\n};\n","import { ElementNode } from 'lexical';\nexport class CommentNode extends ElementNode {\n static getType() {\n return 'comment';\n }\n static clone(node) {\n const cloned = new CommentNode(node.__annotationId, node.__multiThreadAnnotationId, node.__key);\n return cloned;\n }\n constructor(annotationId, multiThreadAnnotationId, key) {\n super(key);\n this.__annotationId = annotationId;\n this.__multiThreadAnnotationId = multiThreadAnnotationId;\n }\n createDOM(config) {\n const dom = document.createElement('velt-comment-text');\n if (this.__annotationId) {\n dom.setAttribute('annotation-id', this.__annotationId);\n }\n if (this.__multiThreadAnnotationId) {\n dom.setAttribute('multi-thread-annotation-id', this.__multiThreadAnnotationId);\n }\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n return false;\n }\n static importJSON(serializedNode) {\n return $createCommentNode(serializedNode.annotationId, serializedNode.multiThreadAnnotationId);\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n annotationId: this.__annotationId,\n multiThreadAnnotationId: this.__multiThreadAnnotationId,\n type: 'comment',\n version: 1,\n };\n }\n isInline() {\n return true;\n }\n}\nexport function $createCommentNode(annotationId, multiThreadAnnotationId) {\n return new CommentNode(annotationId, multiThreadAnnotationId);\n}\nexport function $isCommentNode(node) {\n return node instanceof CommentNode;\n}\n","var _a;\nexport class VeltService {\n}\n_a = VeltService;\nVeltService.selectedCommentsMap = new Map();\nVeltService.subscribers = new Map();\nVeltService.isSubscribed = false;\n/* eslint-disable @typescript-eslint/no-explicit-any */\nVeltService.getCommentElement = () => {\n try {\n const velt = window.Velt;\n if (velt?.getCommentElement) {\n return velt.getCommentElement();\n }\n return null;\n }\n catch (error) {\n _a.catchError('Error getting comment element:', error);\n return null;\n }\n};\nVeltService.addManualComment = ({ context, location, }) => {\n try {\n const commentElement = _a.getCommentElement();\n if (!commentElement)\n return Promise.resolve(null);\n return commentElement.addManualComment({ context, location });\n }\n catch (error) {\n _a.catchError('Error adding manual comment:', error);\n return Promise.resolve(null);\n }\n};\nVeltService.updateContext = ({ annotationId, context, }) => {\n try {\n const commentElement = _a.getCommentElement();\n if (!commentElement)\n return Promise.resolve(null);\n return commentElement.updateContext(annotationId, context);\n }\n catch (error) {\n _a.catchError('Error updating context:', error);\n }\n};\nVeltService.subscribeToSelectedCommentsMap = (id, subscriber) => {\n try {\n _a.subscribeToSelectedAnnotationsMapSingleton();\n _a.subscribers.set(id, subscriber);\n }\n catch (error) {\n _a.catchError('Error subscribing to selected comments map:', error);\n }\n};\nVeltService.unsubscribeFromSelectedCommentsMap = (id) => {\n try {\n _a.subscribers.delete(id);\n }\n catch (error) {\n _a.catchError('Error unsubscribing from selected comments map:', error);\n }\n};\nVeltService.catchError = (message, otherProperties) => {\n try {\n const url = 'https://api.velt.dev/sa';\n let properties = {};\n if (otherProperties && typeof otherProperties === 'object') {\n properties = { ...properties, ...otherProperties };\n }\n if (message) {\n properties.message = message;\n }\n properties.sdkVersion = window.Velt?.version;\n const body = {\n type: 'track',\n data: {\n event: 'lexicalVeltCommentsError',\n properties: properties,\n },\n };\n // fetch(url, {\n // method: 'POST',\n // headers: {\n // 'Content-Type': 'application/json',\n // },\n // body: JSON.stringify(body),\n // });\n }\n catch (error) {\n // handle error here\n }\n};\nVeltService.subscribeToSelectedAnnotationsMapSingleton = () => {\n try {\n if (_a.isSubscribed) {\n return;\n }\n const commentElement = _a.getCommentElement();\n if (commentElement && typeof commentElement?.getSelectedComments === 'function') {\n commentElement?.getSelectedComments()?.subscribe((comments) => {\n const selectedCommentsMap = new Map();\n comments?.forEach((comment) => {\n if (comment?.annotationId) {\n selectedCommentsMap.set(comment?.annotationId, true);\n }\n });\n // Check if the maps are different\n const areMapsEqual = (map1, map2) => {\n if (map1.size !== map2.size)\n return false;\n for (const [key, value] of map1) {\n if (map2.get(key) !== value)\n return false;\n }\n return true;\n };\n // Skip update if maps are equal\n if (areMapsEqual(selectedCommentsMap, _a.selectedCommentsMap || new Map())) {\n return;\n }\n _a.updateSelectedCommentsMap(selectedCommentsMap);\n });\n _a.isSubscribed = true;\n }\n }\n catch (error) {\n return new Map();\n }\n};\nVeltService.updateSelectedCommentsMap = (selectedCommentsMap) => {\n try {\n _a.selectedCommentsMap = selectedCommentsMap;\n _a.subscribers.forEach((subscriber) => {\n if (typeof subscriber === 'function') {\n subscriber(_a.selectedCommentsMap);\n }\n });\n }\n catch (error) {\n _a.catchError('Error updating selected comments map:', error);\n }\n};\n","import { $createTextNode, $getRoot, $isElementNode, $isTextNode } from \"lexical\";\nimport { $createCommentNode, $isCommentNode } from \"./comment-node\";\nimport { VeltService } from \"./velt\";\nexport class UtilityService {\n /**\n * KMP Search implementation\n */\n static kmpSearch(text, pattern, startPos = 0, maxOccurrences) {\n try {\n const positions = [];\n if (!pattern || !text || pattern.length > text.length)\n return positions;\n const lps = UtilityService.computeKMPTable(pattern);\n let i = 0;\n let j = 0;\n while (i < text.length) {\n if (pattern[j] === text[i]) {\n i++;\n j++;\n }\n if (j === pattern.length) {\n positions.push(startPos + i - j);\n j = lps[j - 1];\n if (maxOccurrences && positions.length >= maxOccurrences) {\n break;\n }\n }\n else if (i < text.length && pattern[j] !== text[i]) {\n if (j !== 0) {\n j = lps[j - 1];\n }\n else {\n i++;\n }\n }\n }\n return positions;\n }\n catch (error) {\n return [];\n }\n }\n /**\n * Compute KMP failure function\n */\n static computeKMPTable(pattern) {\n try {\n const lps = new Array(pattern.length).fill(0);\n let len = 0;\n let i = 1;\n while (i < pattern.length) {\n if (pattern[i] === pattern[len]) {\n len++;\n lps[i] = len;\n i++;\n }\n else {\n if (len !== 0) {\n len = lps[len - 1];\n }\n else {\n lps[i] = 0;\n i++;\n }\n }\n }\n return lps;\n }\n catch (error) {\n return [];\n }\n }\n /**\n * Collect all text content including paragraph breaks.\n * This method creates a combined text representation of the entire document,\n * inserting newline characters between paragraphs to maintain text structure\n * for accurate multi-paragraph text matching.\n *\n * @param root - The root Lexical node to traverse\n * @returns Object containing:\n * - combinedText: Complete text with newlines between paragraphs\n * - nodeMap: Array mapping each text node and paragraph break to its position\n */\n static collectTextContent(root) {\n try {\n const nodeMap = [];\n let combinedText = '';\n let currentOffset = 0;\n const traverse = (node, depth = 0) => {\n if ($isTextNode(node)) {\n const text = node.getTextContent();\n const startOffset = currentOffset;\n const endOffset = currentOffset + text.length;\n // Map this text node to its position in combined text\n nodeMap.push({\n node,\n start: startOffset,\n end: endOffset\n });\n combinedText += text;\n currentOffset += text.length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n const nodeType = node.getType();\n // Process all children first\n for (let i = 0; i < children.length; i++) {\n traverse(children[i], depth + 1);\n }\n // Add paragraph break after paragraph elements (except the last one)\n if (nodeType === 'paragraph' && depth === 1) {\n const parent = node.getParent();\n if (parent && $isElementNode(parent)) {\n const siblings = parent.getChildren();\n const nodeIndex = siblings.indexOf(node);\n // Add newline if this is not the last paragraph\n if (nodeIndex < siblings.length - 1) {\n const startOffset = currentOffset;\n const endOffset = currentOffset + 1;\n // Map the paragraph break to its position\n nodeMap.push({\n node: null,\n start: startOffset,\n end: endOffset,\n isParagraphBreak: true\n });\n combinedText += '\\n';\n currentOffset += 1;\n }\n }\n }\n }\n };\n traverse(root);\n return { combinedText, nodeMap };\n }\n catch (error) {\n VeltService.catchError('Error collecting text content:', error);\n return { combinedText: '', nodeMap: [] };\n }\n }\n /**\n * Get text nodes within a specific character range (Updated to handle paragraph breaks)\n */\n static getTextNodesInRange(root, fromPos, toPos, excludeAnnotationId) {\n try {\n const textNodes = [];\n const { combinedText, nodeMap } = UtilityService.collectTextContent(root);\n // Find all text nodes that overlap with the target range\n for (const item of nodeMap) {\n // Skip paragraph breaks and non-text nodes\n if (item.isParagraphBreak || !item.node) {\n continue;\n }\n // Skip comment nodes with the same annotation ID\n const parent = item.node.getParent();\n if ($isCommentNode(parent) && excludeAnnotationId && parent.__annotationId === excludeAnnotationId) {\n continue;\n }\n // Check if this node overlaps with our target range\n if (item.end > fromPos && item.start < toPos) {\n const startOffset = Math.max(0, fromPos - item.start);\n const endOffset = Math.min(item.node.getTextContent().length, toPos - item.start);\n const isPartial = startOffset > 0 || endOffset < item.node.getTextContent().length;\n textNodes.push({\n node: item.node,\n startOffset,\n endOffset,\n isPartial\n });\n }\n }\n return textNodes;\n }\n catch (error) {\n VeltService.catchError('Error getting text nodes in range:', error);\n return [];\n }\n }\n /**\n * Check if a text range is already wrapped (Updated to handle paragraph breaks)\n */\n static isRangeAlreadyWrapped(root, fromPos, toPos, annotationId) {\n try {\n let currentPosition = 0;\n let foundWrapping = false;\n const traverse = (node) => {\n if ($isCommentNode(node)) {\n if (node.__annotationId === annotationId) {\n const text = node.getTextContent();\n const nodeStart = currentPosition;\n const nodeEnd = currentPosition + text.length;\n // Check if this comment node covers our target range\n if (nodeStart <= fromPos && nodeEnd >= toPos) {\n foundWrapping = true;\n return true;\n }\n }\n const text = node.getTextContent();\n currentPosition += text.length;\n }\n else if ($isTextNode(node)) {\n const text = node.getTextContent();\n currentPosition += text.length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n if (traverse(child)) {\n return true;\n }\n }\n // Add paragraph break length if this is a paragraph (except last one)\n const nodeType = node.getType();\n if (nodeType === 'paragraph') {\n const parent = node.getParent();\n if (parent && $isElementNode(parent)) {\n const siblings = parent.getChildren();\n const nodeIndex = siblings.indexOf(node);\n if (nodeIndex < siblings.length - 1) {\n currentPosition += 1; // Add newline length\n }\n }\n }\n }\n return false;\n };\n traverse(root);\n return foundWrapping;\n }\n catch (error) {\n // If there's an error, assume it's wrapped to avoid breaking the flow\n return true;\n }\n }\n /**\n * Find the paragraph parent of a text node.\n * Traverses up the node hierarchy to find the paragraph element that contains this text node.\n *\n * @param node - The text node to find the paragraph parent for\n * @returns The paragraph node or null if not found\n */\n static findParagraphParent(node) {\n let current = node.getParent();\n while (current) {\n if ($isElementNode(current) && current.getType() === 'paragraph') {\n return current;\n }\n current = current.getParent();\n }\n return null;\n }\n /**\n * Get text content for a specific container\n */\n static getContainerTextContent(containerElement, editor, nodeMap, combinedText) {\n try {\n let containerStartOffset = -1;\n let containerEndOffset = -1;\n // Find the range of text that belongs to this container\n for (let i = 0; i < nodeMap.length; i++) {\n const item = nodeMap[i];\n if (item.isParagraphBreak) {\n continue; // Skip paragraph breaks for DOM mapping\n }\n if (item.node) {\n try {\n const nodeKey = item.node.getKey();\n const nodeDOMElement = editor.getElementByKey(nodeKey);\n if (nodeDOMElement && containerElement.contains(nodeDOMElement)) {\n if (containerStartOffset === -1) {\n containerStartOffset = item.start;\n }\n containerEndOffset = item.end;\n }\n }\n catch (error) {\n // Skip nodes that can't be mapped to DOM\n continue;\n }\n }\n }\n if (containerStartOffset === -1 || containerEndOffset === -1) {\n return null;\n }\n const containerText = combinedText.substring(containerStartOffset, containerEndOffset);\n return {\n text: containerText,\n startOffset: containerStartOffset\n };\n }\n catch (error) {\n return null;\n }\n }\n /**\n * Find common parent element with ID that contains both nodes\n */\n static findCommonParentWithId(node1, node2, editorElement) {\n try {\n const MAX_ITERATIONS = 100;\n let iterationCount = 0;\n // Get all ancestors of node1 with IDs\n const ancestors1WithId = [];\n let current1 = node1;\n while (current1 && current1 !== document.body && iterationCount < MAX_ITERATIONS) {\n if (!editorElement.contains(current1)) {\n break;\n }\n if (current1.id) {\n ancestors1WithId.push(current1);\n }\n current1 = current1.parentElement;\n iterationCount++;\n }\n // Find first ancestor of node2 that's also an ancestor of node1 and has ID\n let current2 = node2;\n iterationCount = 0;\n while (current2 && current2 !== document.body && iterationCount < MAX_ITERATIONS) {\n if (!editorElement.contains(current2)) {\n break;\n }\n if (current2.id && ancestors1WithId.includes(current2)) {\n return current2; // Found common ancestor with ID\n }\n current2 = current2.parentElement;\n iterationCount++;\n }\n return null;\n }\n catch (error) {\n return null;\n }\n }\n /**\n * Helper method to find container with ID (fallback)\n */\n static findContainerWithId(startNode, editorElement) {\n try {\n const MAX_ITERATIONS = 100;\n let iterationCount = 0;\n let current = startNode;\n while (current && current !== document.body && iterationCount < MAX_ITERATIONS) {\n if (!editorElement.contains(current)) {\n break;\n }\n if (current.id) {\n return current;\n }\n current = current.parentElement;\n iterationCount++;\n }\n return null;\n }\n catch (error) {\n return null;\n }\n }\n /**\n * Get the text range of current selection, accounting for paragraph breaks.\n * This method maps Lexical selection positions to text offsets in the combined text\n * representation that includes newline characters between paragraphs.\n *\n * @param editor - The Lexical editor instance\n * @param selection - The current range selection\n * @returns Object with start and end positions in the combined text\n */\n static getSelectionTextRange(editor, selection) {\n const root = $getRoot();\n const { nodeMap } = UtilityService.collectTextContent(root);\n // Get the anchor and focus nodes from the selection\n const anchorNode = selection.anchor.getNode();\n const focusNode = selection.focus.getNode();\n const anchorOffset = selection.anchor.offset;\n const focusOffset = selection.focus.offset;\n // Map node positions to text offsets in the combined text\n let anchorTextPos = 0;\n let focusTextPos = 0;\n for (const item of nodeMap) {\n // Skip paragraph breaks for node mapping\n if (item.isParagraphBreak || !item.node) {\n continue;\n }\n const nodeKey = item.node.getKey();\n if (nodeKey === anchorNode.getKey()) {\n anchorTextPos = item.start + anchorOffset;\n }\n if (nodeKey === focusNode.getKey()) {\n focusTextPos = item.start + focusOffset;\n }\n }\n // Return ordered range (start should be less than end)\n return {\n start: Math.min(anchorTextPos, focusTextPos),\n end: Math.max(anchorTextPos, focusTextPos)\n };\n }\n /**\n * Wrap a partial text node (when selection is within the node) - PRESERVING FORMATTING\n */\n static wrapPartialTextNode(textNode, startOffset, endOffset, commentNode) {\n try {\n const writableNode = textNode.getWritable();\n const fullText = writableNode.getTextContent();\n // Split the text into three parts: before, selected, after\n const beforeText = fullText.substring(0, startOffset);\n const selectedText = fullText.substring(startOffset, endOffset);\n const afterText = fullText.substring(endOffset);\n // Create the selected portion as a text node with the same formatting as the original\n const selectedTextNode = $createTextNode(selectedText);\n // Copy formatting from original node to new node\n selectedTextNode.setFormat(writableNode.getFormat());\n selectedTextNode.setStyle(writableNode.getStyle());\n commentNode.append(selectedTextNode);\n // Handle the before and after parts\n if (beforeText) {\n const beforeNode = $createTextNode(beforeText);\n beforeNode.setFormat(writableNode.getFormat());\n beforeNode.setStyle(writableNode.getStyle());\n writableNode.insertBefore(beforeNode);\n }\n writableNode.insertBefore(commentNode);\n if (afterText) {\n const afterNode = $createTextNode(afterText);\n afterNode.setFormat(writableNode.getFormat());\n afterNode.setStyle(writableNode.getStyle());\n writableNode.insertBefore(afterNode);\n }\n // Remove the original node\n writableNode.remove();\n }\n catch (error) {\n // do nothing\n }\n }\n /**\n * Wrap an entire text node - PRESERVING FORMATTING\n */\n static wrapEntireTextNode(textNode, commentNode) {\n try {\n const writableNode = textNode.getWritable();\n // Move the original node into the comment node (preserving all formatting)\n commentNode.append(writableNode);\n // Get the parent and replace the original node position with the comment node\n const parent = writableNode.getParent();\n if (parent) {\n const parentWritable = parent.getWritable();\n const nodeIndex = writableNode.getIndexWithinParent();\n parentWritable.splice(nodeIndex, 1, [commentNode]);\n }\n }\n catch (error) {\n // do nothing\n }\n }\n /**\n * Wrap multiple text nodes while preserving formatting and paragraph structure.\n * This method groups text nodes by their paragraph parent and creates separate\n * comment nodes for each paragraph to maintain document structure integrity.\n *\n * @param textNodesToWrap - Array of text nodes with their offset information\n * @param commentNode - The base comment node to use (additional nodes will be created for other paragraphs)\n */\n static wrapMultipleTextNodesWithFormatting(textNodesToWrap, commentNode) {\n try {\n // Group nodes by their paragraph parent to maintain paragraph structure\n const nodesByParagraph = new Map();\n for (const nodeData of textNodesToWrap) {\n const paragraphParent = UtilityService.findParagraphParent(nodeData.node);\n const paragraphKey = paragraphParent ? paragraphParent.getKey() : 'no-paragraph';\n if (!nodesByParagraph.has(paragraphKey)) {\n nodesByParagraph.set(paragraphKey, []);\n }\n nodesByParagraph.get(paragraphKey).push(nodeData);\n }\n // Create separate comment nodes for each paragraph\n let isFirstParagraph = true;\n for (const [, nodesInParagraph] of nodesByParagraph.entries()) {\n // Create a comment node for this paragraph (reuse the first one, create new ones for others)\n const paragraphCommentNode = isFirstParagraph\n ? commentNode\n : $createCommentNode(commentNode.__annotationId, commentNode.__multiThreadAnnotationId);\n // Process nodes within this paragraph\n if (nodesInParagraph.length === 1) {\n const nodeData = nodesInParagraph[0];\n const { node, startOffset, endOffset, isPartial } = nodeData;\n if (isPartial) {\n UtilityService.wrapPartialTextNode(node, startOffset, endOffset, paragraphCommentNode);\n }\n else {\n UtilityService.wrapEntireTextNode(node, paragraphCommentNode);\n }\n }\n else {\n // Multiple nodes in same paragraph - handle them sequentially\n UtilityService.wrapMultipleNodesInSameParagraph(nodesInParagraph, paragraphCommentNode);\n }\n isFirstParagraph = false;\n }\n }\n catch (error) {\n VeltService.catchError('Error in wrapMultipleTextNodesWithFormatting:', error);\n }\n }\n /**\n * Wrap multiple text nodes within the same paragraph.\n * This method handles complex scenarios where multiple text nodes need to be wrapped\n * while maintaining their original formatting and order within the paragraph.\n *\n * @param nodesInParagraph - Array of text nodes within the same paragraph\n * @param commentNode - The comment node to wrap the text nodes with\n */\n static wrapMultipleNodesInSameParagraph(nodesInParagraph, commentNode) {\n try {\n // Sort nodes by their position in the paragraph\n const sortedNodes = [...nodesInParagraph].sort((a, b) => {\n const aIndex = a.node.getIndexWithinParent();\n const bIndex = b.node.getIndexWithinParent();\n return aIndex - bIndex;\n });\n const firstNodeData = sortedNodes[0];\n const lastNodeData = sortedNodes[sortedNodes.length - 1];\n // Handle first node\n const firstNode = firstNodeData.node.getWritable();\n const parent = firstNode.getParent();\n if (!parent)\n return;\n const parentWritable = parent.getWritable();\n let insertionIndex = firstNode.getIndexWithinParent();\n // Process first node\n if (firstNodeData.isPartial && firstNodeData.startOffset > 0) {\n // Split the first node - keep text before selection separate\n const beforeText = firstNodeData.node.getTextContent().substring(0, firstNodeData.startOffset);\n const selectedText = firstNodeData.node.getTextContent().substring(firstNodeData.startOffset);\n const beforeNode = $createTextNode(beforeText);\n beforeNode.setFormat(firstNode.getFormat());\n beforeNode.setStyle(firstNode.getStyle());\n const selectedPartNode = $createTextNode(selectedText);\n selectedPartNode.setFormat(firstNode.getFormat());\n selectedPartNode.setStyle(firstNode.getStyle());\n commentNode.append(selectedPartNode);\n // Replace original node with before node and comment node\n parentWritable.splice(insertionIndex, 1, [beforeNode, commentNode]);\n }\n else {\n // Replace entire first node with comment node (move content into comment)\n commentNode.append(firstNode);\n parentWritable.splice(insertionIndex, 1, [commentNode]);\n }\n // Process middle nodes (if any) - move them into the comment node\n for (let i = 1; i < sortedNodes.length - 1; i++) {\n const nodeData = sortedNodes[i];\n const writableNode = nodeData.node.getWritable();\n // Move the entire middle node into comment node (preserves formatting)\n commentNode.append(writableNode);\n // Node is automatically removed from its original parent when moved\n }\n // Process last node (if different from first)\n if (sortedNodes.length > 1) {\n const lastNode = lastNodeData.node.getWritable();\n if (lastNodeData.isPartial && lastNodeData.endOffset < lastNodeData.node.getTextContent().length) {\n // Split the last node - keep text after selection separate\n const selectedText = lastNodeData.node.getTextContent().substring(0, lastNodeData.endOffset);\n const afterText = lastNodeData.node.getTextContent().substring(lastNodeData.endOffset);\n const selectedPartNode = $createTextNode(selectedText);\n selectedPartNode.setFormat(lastNode.getFormat());\n selectedPartNode.setStyle(lastNode.getStyle());\n const afterNode = $createTextNode(afterText);\n afterNode.setFormat(lastNode.getFormat());\n afterNode.setStyle(lastNode.getStyle());\n commentNode.append(selectedPartNode);\n // Replace the last node with just the remaining text\n lastNode.replace(afterNode);\n }\n else {\n // Move entire last node into comment\n commentNode.append(lastNode);\n // Node is automatically removed from its original parent when moved\n }\n }\n }\n catch (error) {\n VeltService.catchError('Error in wrapMultipleNodesInSameParagraph:', error);\n }\n }\n}\n","import { $createTextNode, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode } from 'lexical';\nimport { $createCommentNode, $isCommentNode } from './comment-node';\nimport { UtilityService } from './utility';\nimport { VeltService } from './velt';\nexport class EditorService {\n constructor(plugin) {\n this.plugin = plugin;\n }\n /**\n * Initialize the transaction listener (equivalent to TipTap's onTransaction)\n * Call this method when setting up the plugin\n */\n initializeTransactionListener(editor) {\n try {\n // Remove existing listener if any\n if (this.editorUpdateListenerRemover) {\n this.editorUpdateListenerRemover();\n }\n // Register update listener (equivalent to TipTap's onTransaction)\n this.editorUpdateListenerRemover = editor.registerUpdateListener(({ editorState, prevEditorState }) => {\n try {\n // Only process if the document content actually changed\n if (editorState === prevEditorState || !this.hasDocumentChanged(editorState, prevEditorState)) {\n return;\n }\n this.onTransaction(editor);\n }\n catch (error) {\n // do nothing\n }\n });\n }\n catch (err) {\n VeltService.catchError('Error initializing transaction listener:', err);\n }\n }\n /**\n * Clean up the transaction listener\n */\n destroyTransactionListener() {\n try {\n // Remove the listener\n if (this.editorUpdateListenerRemover) {\n this.editorUpdateListenerRemover();\n this.editorUpdateListenerRemover = undefined;\n }\n }\n catch (err) {\n VeltService.catchError('Error destroying transaction listener:', err);\n }\n }\n /**\n * Check if the document content has actually changed\n */\n hasDocumentChanged(currentState, prevState) {\n try {\n return currentState.read(() => {\n const currentText = $getRoot().getTextContent();\n return prevState.read(() => {\n const prevText = $getRoot().getTextContent();\n return currentText !== prevText;\n });\n });\n }\n catch (error) {\n return true; // Assume changed if we can't determine\n }\n }\n /**\n * Process editor changes\n */\n onTransaction(editor) {\n try {\n const changedAnnotationContent = new Map();\n // Phase 1: Collect all annotations in the document\n const annotationsToCheck = this.collectDocumentAnnotations(editor);\n // Phase 2: Process each annotation to check for changes\n for (const [annotationId, data] of Array.from(annotationsToCheck.entries())) {\n const changes = this.processAnnotationChanges(editor, data);\n if (changes) {\n changedAnnotationContent.set(annotationId, changes);\n }\n }\n // Phase 3: Apply updates for any annotations that changed\n if (changedAnnotationContent.size > 0) {\n const commentElement = VeltService.getCommentElement();\n if (!commentElement)\n return;\n this.updateVeltComments(changedAnnotationContent, editor);\n }\n }\n catch (error) {\n VeltService.catchError('Error in onTransaction:', error);\n }\n }\n /**\n * Extract text from a CommentNode while preserving paragraph structure.\n * This method ensures that text extracted from existing comment nodes maintains\n * the same newline characters that were present during initial selection.\n *\n * @param commentNode - The CommentNode to extract text from\n * @returns Text content with preserved paragraph breaks (newlines)\n */\n getCommentNodeTextWithStructure(commentNode) {\n try {\n let text = '';\n const traverse = (node, depth = 0) => {\n if ($isTextNode(node)) {\n text += node.getTextContent();\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n const nodeType = node.getType();\n // Process all children first\n for (let i = 0; i < children.length; i++) {\n traverse(children[i], depth + 1);\n }\n // Add paragraph break after paragraph elements (except the last one)\n // This mirrors the logic in collectTextContent\n if (nodeType === 'paragraph' && depth >= 0) {\n const parent = node.getParent();\n if (parent && $isElementNode(parent)) {\n const siblings = parent.getChildren();\n const nodeIndex = siblings.indexOf(node);\n // Add newline if this is not the last paragraph\n if (nodeIndex < siblings.length - 1) {\n text += '\\n';\n }\n }\n }\n }\n };\n traverse(commentNode);\n return text;\n }\n catch (error) {\n // Fallback to original method if reconstruction fails\n return commentNode.getTextContent();\n }\n }\n /**\n * Collect all annotations present in the document for processing\n */\n collectDocumentAnnotations(editor) {\n try {\n const annotationsToCheck = new Map();\n // Capture 'this' context before entering the nested scope\n const self = this;\n editor.getEditorState().read(() => {\n const root = $getRoot();\n let currentPosition = 0;\n // Use arrow function to preserve 'this' context\n const traverse = (node) => {\n if ($isCommentNode(node)) {\n const annotationId = node.__annotationId;\n if (!annotationId)\n return;\n // Now we can access 'this' properly through 'self'\n const prevAnnotationData = self.plugin.storeService.annotationStore.get(annotationId);\n if (!prevAnnotationData)\n return;\n // Use consistent text collection that preserves paragraph structure\n const text = self.getCommentNodeTextWithStructure(node);\n const nodeSize = text.length;\n // Check if we've already processed this annotation\n const existingAnnotation = annotationsToCheck.get(annotationId);\n const nodeData = {\n node: { text, nodeSize },\n pos: currentPosition,\n };\n if (existingAnnotation) {\n existingAnnotation.nodes.push(nodeData);\n }\n else {\n annotationsToCheck.set(annotationId, {\n id: annotationId,\n multiThreadAnnotationId: node.__multiThreadAnnotationId,\n annotation: prevAnnotationData.annotation,\n nodes: [nodeData],\n originalText: prevAnnotationData.annotation.context?.textEditorConfig?.text || '',\n originalOccurrence: prevAnnotationData.annotation.context?.textEditorConfig?.occurrence || 1,\n targetTextNodeId: prevAnnotationData.annotation.context?.textEditorConfig?.targetTextNodeId || '',\n });\n }\n currentPosition += nodeSize;\n }\n else if ($isTextNode(node)) {\n currentPosition += node.getTextContent().length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n };\n traverse(root);\n });\n return annotationsToCheck;\n }\n catch (error) {\n VeltService.catchError('Error collecting document annotations:', error);\n return new Map();\n }\n }\n /**\n * Process annotation changes\n */\n processAnnotationChanges(editor, data) {\n try {\n // Input validation\n if (!editor || !data) {\n throw new Error('Invalid input: editor and data are required');\n }\n if (!data.originalText || !data.annotation) {\n return null;\n }\n if (!Array.isArray(data.nodes) || data.nodes.length === 0) {\n return null;\n }\n // Step 1: Detect content changes\n const { currentText, contentChanged } = this.detectContentChanges(data.nodes, data.originalText);\n // Step 2: Detect container changes and get updated search results\n const { targetTextNodeIdChanged, newTargetTextNodeId, searchResults } = this.detectContainerChanges(editor, {\n annotationId: data.id,\n currentText,\n targetTextNodeId: data.targetTextNodeId,\n nodes: data.nodes,\n });\n // Step 3: Calculate occurrence changes\n const currentOccurrence = this.calculateNewOccurrence(editor, {\n searchResults,\n nodes: data.nodes,\n contentChanged,\n currentText,\n targetTextNodeId: targetTextNodeIdChanged ? newTargetTextNodeId : data.targetTextNodeId,\n originalOccurrence: data.originalOccurrence,\n });\n const occurrenceChanged = currentOccurrence !== data.originalOccurrence;\n // Step 4: Create and return change object if any changes detected\n if (contentChanged || occurrenceChanged || targetTextNodeIdChanged) {\n return this.createAnnotationChange(data, {\n currentText,\n contentChanged,\n currentOccurrence,\n occurrenceChanged,\n targetTextNodeIdChanged,\n newTargetTextNodeId,\n });\n }\n return null;\n }\n catch (error) {\n VeltService.catchError('Error processing annotation changes:', error);\n return null;\n }\n }\n /**\n * Detect content changes while preserving paragraph structure.\n * When multiple CommentNodes represent different paragraphs of the same annotation,\n * we need to join them with newlines to match the original text format.\n */\n detectContentChanges(nodes, originalText) {\n const sortedNodes = [...nodes].sort((a, b) => a.pos - b.pos);\n // If we have multiple nodes (indicating multiple paragraphs), join with newlines\n // Otherwise, join normally\n const currentText = sortedNodes.length > 1 && originalText.includes('\\n')\n ? sortedNodes.map((n) => n.node.text || '').join('\\n')\n : sortedNodes.map((n) => n.node.text || '').join('');\n return {\n currentText,\n contentChanged: currentText !== originalText,\n };\n }\n /**\n * Detect container changes\n */\n detectContainerChanges(editor, params) {\n const { annotationId, currentText, targetTextNodeId, nodes } = params;\n let targetTextNodeIdChanged = false;\n let newTargetTextNodeId = targetTextNodeId;\n let searchResults = [];\n // First try to find text in the current container\n const containerChangeResult = this.processContainerChanges(editor, {\n annotationId,\n currentText,\n targetTextNodeId,\n nodes,\n });\n if (containerChangeResult) {\n targetTextNodeIdChanged = containerChangeResult.changed;\n newTargetTextNodeId = containerChangeResult.newId;\n searchResults = containerChangeResult.searchResults;\n }\n else {\n // No container change, search in current or document context\n searchResults = targetTextNodeId\n ? this.findTextInDomElement(editor, currentText, targetTextNodeId, undefined)\n : this.findTextInDocument(editor, currentText, undefined);\n }\n return {\n targetTextNodeIdChanged,\n newTargetTextNodeId,\n searchResults,\n };\n }\n /**\n * Process container changes\n */\n processContainerChanges(editor, params) {\n const { annotationId, currentText, targetTextNodeId } = params;\n if (!targetTextNodeId)\n return null;\n // Find the comment node with matching annotation ID\n let foundNodePosition = null;\n editor.getEditorState().read(() => {\n const root = $getRoot();\n let currentPos = 0;\n function traverse(node) {\n if ($isCommentNode(node) && node.__annotationId === annotationId) {\n foundNodePosition = currentPos;\n return;\n }\n if ($isTextNode(node)) {\n currentPos += node.getTextContent().length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n if (foundNodePosition !== null)\n return;\n }\n }\n }\n traverse(root);\n });\n if (foundNodePosition !== null) {\n // Find the corresponding DOM element and check its container\n const editorDOMElement = editor.getRootElement();\n if (!editorDOMElement)\n return null;\n // This is a simplified approach - in practice you'd want more precise DOM mapping\n const currentContainer = editorDOMElement.querySelector(`#${targetTextNodeId}`);\n if (currentContainer) {\n // Check if content is still within the same container\n const searchResults = this.findTextInDomElement(editor, currentText, targetTextNodeId, undefined);\n if (searchResults.length === 0) {\n // Content moved to a different container, try to find new container\n const newContainer = this.findNewContainer(editor, currentText);\n if (newContainer && newContainer.id !== targetTextNodeId) {\n const newSearchResults = this.findTextInDomElement(editor, currentText, newContainer.id, undefined);\n return {\n changed: true,\n newId: newContainer.id,\n searchResults: newSearchResults,\n };\n }\n }\n }\n }\n return null;\n }\n /**\n * Find new container for moved content\n */\n findNewContainer(editor, currentText) {\n const editorElement = editor.getRootElement();\n if (!editorElement)\n return null;\n // Find all elements with IDs that contain the text\n const elementsWithIds = editorElement.querySelectorAll('[id]');\n for (const element of Array.from(elementsWithIds)) {\n if (element.textContent?.includes(currentText)) {\n return element;\n }\n }\n return null;\n }\n /**\n * Calculate new occurrence\n */\n calculateNewOccurrence(editor, params) {\n if (params.searchResults.length === 0) {\n return params.originalOccurrence;\n }\n const firstNode = params.nodes[0];\n const lastNode = params.nodes[params.nodes.length - 1];\n const annotationRange = {\n from: firstNode.pos,\n to: lastNode.pos + (lastNode.node.nodeSize || 0),\n };\n for (let i = 0; i < params.searchResults.length; i++) {\n const result = params.searchResults[i];\n if ((result.start <= annotationRange.from && result.end >= annotationRange.from) ||\n (result.start <= annotationRange.to && result.end >= annotationRange.to) ||\n (annotationRange.from <= result.start && annotationRange.to >= result.end)) {\n return i + 1;\n }\n }\n return params.originalOccurrence;\n }\n /**\n * Create annotation change object\n */\n createAnnotationChange(data, changes) {\n const { currentText, contentChanged, currentOccurrence, occurrenceChanged, targetTextNodeIdChanged, newTargetTextNodeId, } = changes;\n return {\n annotationId: data.id,\n multiThreadAnnotationId: data.multiThreadAnnotationId,\n originalText: data.originalText,\n currentText: contentChanged ? currentText : data.originalText,\n originalOccurrence: data.originalOccurrence,\n currentOccurrence,\n originalTargetTextNodeId: data.targetTextNodeId,\n newTargetTextNodeId: targetTextNodeIdChanged ? newTargetTextNodeId : data.targetTextNodeId,\n annotation: data.annotation,\n contentChanged,\n occurrenceChanged,\n targetTextNodeIdChanged,\n };\n }\n /**\n * Update Velt comments\n */\n async updateVeltComments(changes, editor) {\n try {\n for (const [annotationId, changeData] of Array.from(changes.entries())) {\n try {\n if (!changeData.annotation.context?.textEditorConfig)\n continue;\n const textEditorConfig = JSON.parse(JSON.stringify(changeData.annotation.context.textEditorConfig));\n if (changeData.contentChanged) {\n textEditorConfig.text = changeData.currentText;\n }\n if (changeData.occurrenceChanged) {\n textEditorConfig.occurrence = changeData.currentOccurrence;\n }\n if (changeData.targetTextNodeIdChanged) {\n textEditorConfig.targetTextNodeId = changeData.newTargetTextNodeId;\n }\n const context = {\n ...changeData.annotation.context,\n textEditorConfig,\n };\n // Remove data from store\n this.plugin.storeService.annotationStore.remove(annotationId);\n this.removeVeltCommentFromEditor(editor, annotationId);\n // delay 100ms\n await new Promise(resolve => setTimeout(resolve, 100));\n VeltService.updateContext({ annotationId, context });\n }\n catch (err) {\n VeltService.catchError('Error updating individual Velt comment:', err);\n }\n }\n }\n catch (err) {\n VeltService.catchError('Error updating Velt comments:', err);\n }\n }\n getSelectedText(editor) {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n return $isRangeSelection(selection) ? selection.getTextContent() : '';\n });\n }\n /**\n * Find occurrence index of selected text in Lexical editor.\n * This method determines which occurrence of a text pattern the current selection represents,\n * which is crucial for multi-paragraph text selections that span across different paragraphs.\n *\n * @param editor - The Lexical editor instance\n * @param selectedText - The text that is currently selected (may include newlines for multi-paragraph selections)\n * @param targetContainer - Optional container element to limit search scope\n * @returns The 1-indexed occurrence number of the selected text\n */\n findOccurrenceIndex(editor, selectedText, targetContainer) {\n try {\n if (!selectedText.trim()) {\n return 1;\n }\n let matches = [];\n let selectionStart = 0;\n let selectionEnd = 0;\n editor.getEditorState().read(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n // Get current selection range in terms of text offset (including paragraph breaks)\n const range = UtilityService.getSelectionTextRange(editor, selection);\n selectionStart = range.start;\n selectionEnd = range.end;\n // Find all matches in the specified scope\n matches = targetContainer && targetContainer.id\n ? this.findTextInContainer(editor, selectedText, targetContainer.id)\n : this.findTextInEditor(editor, selectedText);\n }\n });\n // Find which occurrence matches our current selection position\n for (let i = 0; i < matches.length; i++) {\n if (matches[i].start === selectionStart && matches[i].end === selectionEnd) {\n return i + 1; // Return 1-indexed occurrence\n }\n }\n return 1;\n }\n catch (error) {\n VeltService.catchError('Error finding occurrence index:', error);\n return 1;\n }\n }\n /**\n * Find all text occurrences in the entire Lexical editor.\n * This method supports multi-paragraph text search by using the combined text\n * representation that includes newline characters between paragraphs.\n *\n * @param editor - The Lexical editor instance\n * @param searchText - The text to search for (may include newlines for multi-paragraph searches)\n * @returns Array of TextMatch objects with start and end positions\n */\n findTextInEditor(editor, searchText) {\n try {\n const matches = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = UtilityService.collectTextContent(root);\n // Use KMP search on the combined text (which includes newlines)\n const positions = UtilityService.kmpSearch(combinedText, searchText);\n // Convert string positions to TextMatch objects\n for (const pos of positions) {\n matches.push({\n start: pos,\n end: pos + searchText.length\n });\n }\n });\n return matches;\n }\n catch (error) {\n VeltService.catchError('Error finding text in editor:', error);\n return [];\n }\n }\n /**\n * Find text occurrences within a specific DOM container (Updated to handle paragraph breaks)\n */\n findTextInContainer(editor, searchText, containerId) {\n try {\n const matches = [];\n const containerElement = document.getElementById(containerId);\n if (!containerElement) {\n return matches;\n }\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText, nodeMap } = UtilityService.collectTextContent(root);\n // Filter content that belongs to the container\n const containerContent = UtilityService.getContainerTextContent(containerElement, editor, nodeMap, combinedText);\n if (!containerContent) {\n return;\n }\n const positions = UtilityService.kmpSearch(containerContent.text, searchText);\n // Convert positions to global text offsets\n for (const pos of positions) {\n matches.push({\n start: containerContent.startOffset + pos,\n end: containerContent.startOffset + pos + searchText.length\n });\n }\n });\n return matches;\n }\n catch (error) {\n VeltService.catchError('Error finding text in container:', error);\n return [];\n }\n }\n /**\n * Find parent DOM element with ID that contains the entire selection.\n * This method is used for container-level comment scoping and ensures that\n * the returned container actually encompasses the full selected text range.\n *\n * @param editor - The Lexical editor instance\n * @returns HTMLElement with ID that contains entire selection, or null if not found\n */\n findParentNodeWithId(editor) {\n try {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return null;\n }\n const anchorNode = selection.anchor.getNode();\n const focusNode = selection.focus.getNode();\n const editorElement = editor.getRootElement();\n if (!editorElement) {\n return null;\n }\n // Get DOM elements for both anchor and focus nodes\n let anchorDOMNode = null;\n let focusDOMNode = null;\n // Get DOM element for anchor node\n try {\n const anchorKey = anchorNode.getKey();\n anchorDOMNode = editor.getElementByKey(anchorKey);\n if (!anchorDOMNode) {\n const parentKey = anchorNode.getParent()?.getKey();\n if (parentKey) {\n anchorDOMNode = editor.getElementByKey(parentKey);\n }\n }\n }\n catch (error) {\n // Continue without anchor DOM node\n }\n // Get DOM element for focus node \n try {\n const focusKey = focusNode.getKey();\n focusDOMNode = editor.getElementByKey(focusKey);\n if (!focusDOMNode) {\n const parentKey = focusNode.getParent()?.getKey();\n if (parentKey) {\n focusDOMNode = editor.getElementByKey(parentKey);\n }\n }\n }\n catch (error) {\n // Continue without focus DOM node\n }\n // If we can't get DOM nodes, fallback to anchor-only approach\n if (!anchorDOMNode && !focusDOMNode) {\n const fallbackNode = editorElement.querySelector('p');\n return UtilityService.findContainerWithId(fallbackNode, editorElement);\n }\n // Find common parent that contains both selection endpoints\n const startNode = anchorDOMNode || focusDOMNode;\n const endNode = focusDOMNode || anchorDOMNode;\n if (!startNode || !endNode) {\n return null;\n }\n // If both nodes are the same, find container for single node\n if (startNode === endNode) {\n return UtilityService.findContainerWithId(startNode, editorElement);\n }\n // Find the deepest common ancestor that has an ID\n let commonParent = UtilityService.findCommonParentWithId(startNode, endNode, editorElement);\n if (commonParent) {\n // For multi-paragraph selections, be conservative\n // Only return container if it's not a paragraph-level container\n const selectedText = selection.getTextContent();\n // Check if this is a cross-paragraph selection\n if (selectedText.includes('\\n')) {\n // Only use container if it's a higher-level container (not individual paragraphs)\n if (commonParent.tagName.toLowerCase() === 'p') {\n return null;\n }\n // For higher-level containers, verify they contain the text\n const containerText = commonParent.textContent || '';\n const normalizedSelected = selectedText.replace(/\\s+/g, ' ').trim();\n const normalizedContainer = containerText.replace(/\\s+/g, ' ').trim();\n if (normalizedContainer.includes(normalizedSelected)) {\n return commonParent;\n }\n else {\n return null;\n }\n }\n else {\n // Single paragraph selection - verify container contains the text\n const containerText = commonParent.textContent || '';\n if (containerText.includes(selectedText)) {\n return commonParent;\n }\n else {\n return null;\n }\n }\n }\n return null;\n });\n }\n catch (error) {\n VeltService.catchError('Error finding parent node with ID:', error);\n return null;\n }\n }\n /**\n * Highlights text in the editor with a Velt comment annotation.\n * This method supports multi-paragraph text highlighting by preserving paragraph structure\n * and creating separate comment nodes for each paragraph when necessary.\n *\n * @param editor - The Lexical editor instance\n * @param textToFind - The text to highlight (may include newlines for multi-paragraph selections)\n * @param commentOptions - Configuration options for the comment annotation\n */\n highlightTextWithVeltComment(editor, textToFind, commentOptions) {\n try {\n const { targetTextNodeId } = commentOptions;\n const desiredOccurrence = commentOptions?.occurrence || 1;\n // Check if newTextEditorConfig is different from stored annotation textEditorConfig\n if (commentOptions.annotationId) {\n const storedData = this.plugin.storeService.annotationStore.get(commentOptions.annotationId);\n if (storedData) {\n const storedConfig = storedData.annotation.context?.textEditorConfig;\n const newConfig = commentOptions.originalAnnotation?.context?.textEditorConfig;\n if (storedConfig &&\n newConfig &&\n (storedConfig.text !== newConfig.text ||\n storedConfig.occurrence !== newConfig.occurrence ||\n storedConfig.targetTextNodeId !== newConfig.targetTextNodeId)) {\n this.removeVeltCommentFromEditor(editor, commentOptions.annotationId);\n }\n }\n }\n if (!textToFind || textToFind.length === 0) {\n return;\n }\n // Validate location if targetTextNodeId is provided\n if (targetTextNodeId) {\n const domNode = document.getElementById(targetTextNodeId);\n if (domNode) {\n const locationId = domNode\n .closest(`[data-velt-location-id]`) // Adjust this attribute as per your Constants\n ?.getAttribute('data-velt-location-id');\n if (locationId !== commentOptions.originalAnnotation?.location?.id) {\n return;\n }\n }\n }\n // Find text matches in the appropriate scope\n let matches = [];\n if (targetTextNodeId) {\n matches = this.findTextInDomElement(editor, textToFind, targetTextNodeId, desiredOccurrence);\n }\n else {\n matches = this.findTextInDocument(editor, textToFind, desiredOccurrence);\n }\n if (matches.length === 0) {\n return;\n }\n const occurrence = Math.min(desiredOccurrence, matches.length);\n const matchToConsider = matches[occurrence - 1];\n if (matchToConsider && commentOptions.annotationId) {\n // Check if this range is already wrapped with the same annotation ID\n const isAlreadyWrapped = editor.getEditorState().read(() => {\n const root = $getRoot();\n return UtilityService.isRangeAlreadyWrapped(root, matchToConsider.start, matchToConsider.end, commentOptions.annotationId);\n });\n // If already wrapped with the same annotation ID, skip wrapping\n if (isAlreadyWrapped) {\n return;\n }\n // Store annotation data\n this.plugin.storeService.annotationStore.set(commentOptions.annotationId, {\n annotation: commentOptions.originalAnnotation || {\n annotationId: commentOptions.annotationId,\n multiThreadAnnotationId: commentOptions.multiThreadAnnotationId,\n text: textToFind,\n },\n pluginReference: {\n node: document.createElement('velt-comment-text'),\n position: { from: matchToConsider.start, to: matchToConsider.end },\n },\n });\n // Apply highlighting in Lexical with multi-paragraph support\n this.setVeltComment(editor, commentOptions.annotationId, commentOptions.multiThreadAnnotationId, matchToConsider.start, matchToConsider.end);\n }\n }\n catch (error) {\n VeltService.catchError('Error highlighting text with Velt comment:', error);\n }\n }\n /**\n * Find text within a specific DOM element by ID.\n * This method is equivalent to TipTap's findTextInDomElement and supports\n * container-level text searching for scoped comment occurrence calculation.\n *\n * @param editor - The Lexical editor instance\n * @param text - The text to search for\n * @param domElementId - The ID of the DOM container element\n * @param desiredOccurrence - Optional limit on number of matches to find\n * @returns Array of TextMatch objects with start/end positions\n */\n findTextInDomElement(editor, text, domElementId, desiredOccurrence) {\n try {\n const matches = [];\n const domNode = document.getElementById(domElementId);\n if (!domNode) {\n return matches;\n }\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText, nodeMap } = UtilityService.collectTextContent(root);\n // Filter content that belongs to the container\n const containerContent = UtilityService.getContainerTextContent(domNode, editor, nodeMap, combinedText);\n if (!containerContent) {\n return;\n }\n // Search within the container's text\n const positions = UtilityService.kmpSearch(containerContent.text, text, 0, desiredOccurrence);\n // Convert container-relative positions to global text positions\n for (const pos of positions) {\n matches.push({\n start: containerContent.startOffset + pos,\n end: containerContent.startOffset + pos + text.length\n });\n }\n });\n return matches;\n }\n catch (error) {\n return [];\n }\n }\n /**\n * Find text in the entire document with support for multi-paragraph searches.\n * This method is used by the highlighting system to locate text that needs to be wrapped\n * with comment annotations, including text that spans across multiple paragraphs.\n *\n * @param editor - The Lexical editor instance\n * @param text - The text to search for (may include newlines for multi-paragraph searches)\n * @param desiredOccurrence - Optional limit on the number of occurrences to find\n * @returns Array of TextMatch objects with start and end positions\n */\n findTextInDocument(editor, text, desiredOccurrence) {\n try {\n const matches = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = UtilityService.collectTextContent(root);\n // Use KMP search on the combined text (which includes newlines)\n const positions = UtilityService.kmpSearch(combinedText, text, 0, desiredOccurrence);\n // Convert string positions to TextMatch objects\n for (const pos of positions) {\n matches.push({\n start: pos,\n end: pos + text.length\n });\n }\n });\n return matches;\n }\n catch (error) {\n VeltService.catchError('Error finding text in document:', error);\n return [];\n }\n }\n /**\n * Creates a Velt comment highlight at the specified range.\n * This method intelligently handles different scenarios including single text nodes,\n * partial text nodes, and multi-paragraph selections while preserving document structure.\n *\n * @param editor - The Lexical editor instance\n * @param annotationId - The annotation ID for the comment\n * @param multiThreadAnnotationId - Optional multi-thread annotation ID\n * @param fromPos - Start position in the combined text representation\n * @param toPos - End position in the combined text representation\n */\n setVeltComment(editor, annotationId, multiThreadAnnotationId, fromPos, toPos) {\n try {\n editor.update(() => {\n const root = $getRoot();\n // Clear any existing selection to prevent selection anchor issues during node manipulation\n // root.selectStart();\n // Get text nodes in the specified range, excluding already wrapped nodes\n const textNodesToWrap = UtilityService.getTextNodesInRange(root, fromPos, toPos, annotationId);\n if (textNodesToWrap.length === 0) {\n return;\n }\n // Create comment node with annotation ID\n const commentNode = $createCommentNode(annotationId, multiThreadAnnotationId);\n // Handle different scenarios based on how many text nodes are involved\n if (textNodesToWrap.length === 1) {\n const { node, startOffset, endOffset, isPartial } = textNodesToWrap[0];\n if (isPartial) {\n // Split the text node and wrap only the selected part\n UtilityService.wrapPartialTextNode(node, startOffset, endOffset, commentNode);\n }\n else {\n // Wrap the entire text node\n UtilityService.wrapEntireTextNode(node, commentNode);\n }\n }\n else {\n // Multiple text nodes - preserve paragraph structure and formatting\n UtilityService.wrapMultipleTextNodesWithFormatting(textNodesToWrap, commentNode);\n }\n });\n }\n catch (error) {\n VeltService.catchError('Error setting Velt comment:', error);\n }\n }\n /**\n * Removes a Velt comment from the Lexical document\n * @param editor - The Lexical editor instance\n * @param annotationId - The ID of the annotation to remove\n * @returns boolean - True if the annotation was successfully removed, false otherwise\n * @throws Error if the stored data is invalid\n */\n removeVeltCommentFromEditor(editor, annotationId) {\n try {\n // Validate input\n if (!editor || !annotationId) {\n throw new Error('Invalid input: editor and annotationId are required');\n }\n // Track if we found and removed any comment nodes\n let nodesRemoved = false;\n // Use Lexical's update mechanism for atomic changes (equivalent to TipTap's editor.chain())\n editor.update(() => {\n const root = $getRoot();\n const commentNodesToRemove = [];\n // Traverse all nodes to find CommentNodes (equivalent to TipTap's doc.descendants)\n function traverse(node) {\n if ($isCommentNode(node)) {\n // Check if this comment node has the matching annotation ID\n if (node.__annotationId === annotationId) {\n commentNodesToRemove.push(node);\n }\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n }\n traverse(root);\n // Remove the found CommentNodes (equivalent to TipTap's tr.removeMark)\n for (const commentNode of commentNodesToRemove) {\n try {\n const writableNode = commentNode.getWritable();\n // Get all child nodes from the comment node\n const children = writableNode.getChildren();\n if (children.length > 0) {\n // Replace comment node with its children (preserving content and formatting)\n const firstChild = children[0];\n // Replace the comment node with the first child\n writableNode.replace(firstChild);\n // Insert remaining children after the first one\n let insertAfter = firstChild;\n for (let i = 1; i < children.length; i++) {\n insertAfter.insertAfter(children[i]);\n insertAfter = children[i];\n }\n }\n else {\n // If no children, check if there's text content\n const textContent = writableNode.getTextContent();\n if (textContent) {\n // Create a text node with the content\n const textNode = $createTextNode(textContent);\n writableNode.replace(textNode);\n }\n else {\n // If no content, just remove the node\n writableNode.remove();\n }\n }\n nodesRemoved = true;\n }\n catch (nodeError) {\n VeltService.catchError('Error removing individual comment node:', nodeError);\n // Continue with other nodes even if one fails\n }\n }\n });\n // Clean up from annotation store if nodes were removed\n if (nodesRemoved) {\n this.plugin.storeService.annotationStore.remove(annotationId);\n }\n return nodesRemoved;\n }\n catch (error) {\n // VeltService.catchError('Error removing Velt comment from Lexical editor:', error);\n return false;\n }\n }\n}\nEditorService.getEditorId = (editor) => {\n try {\n if (!editor)\n return null;\n if (!editor._config)\n return null;\n return editor._config.namespace;\n }\n catch (err) {\n VeltService.catchError('Error finding editor ID:', err);\n return null;\n }\n};\n","import { VeltService } from './velt';\nexport class StoreService {\n constructor(plugin) {\n this.isSubscribed = false;\n this.globalStore = {\n editor: null,\n comments: [],\n filteredComments: [],\n selectedCommentsMap: new Map(),\n };\n // Global annotation storage\n this.globalAnnotationStore = new Map();\n /**\n * Helper to get and set annotations with debug messages\n */\n this.annotationStore = {\n get: (id) => {\n return this.globalAnnotationStore.get(id);\n },\n set: (id, data) => {\n this.globalAnnotationStore.set(id, data);\n },\n getAll: () => {\n return new Map(this.globalAnnotationStore);\n },\n clear: () => {\n this.globalAnnotationStore.clear();\n },\n remove: (id) => {\n this.globalAnnotationStore.delete(id);\n },\n hasAnnotationTextChanged: (annotationId, currentAnnotation) => {\n try {\n const storedData = this.globalAnnotationStore.get(annotationId);\n if (!storedData || !currentAnnotation)\n return false;\n const storedText = storedData.annotation.text;\n const currentText = currentAnnotation.context?.textEditorConfig?.text;\n return storedText !== currentText;\n }\n catch (error) {\n return false;\n }\n },\n };\n this.updateGlobalStore = (store) => {\n try {\n const comments = store?.comments || this.globalStore?.comments || [];\n const newSelectedCommentsMap = store?.selectedCommentsMap || this.globalStore?.selectedCommentsMap || new Map();\n const oldFilteredComments = this.globalStore?.filteredComments || [];\n const filteredComments = comments.filter((annotation) => {\n return (annotation?.status?.type !== 'terminal' ||\n newSelectedCommentsMap?.get(annotation?.annotationId));\n });\n // Create a Set of new annotation IDs for O(1) lookup\n const newAnnotationIds = new Set(filteredComments.map((annotation) => annotation?.annotationId));\n // Get IDs of removed comments\n const removedAnnotationIds = oldFilteredComments\n .map((annotation) => annotation?.annotationId)\n .filter((id) => id && !newAnnotationIds.has(id));\n // Remove comments from editor using just IDs\n removedAnnotationIds.forEach((annotationId) => {\n if (this.globalStore?.editor && annotationId) {\n this.plugin.editorService.removeVeltCommentFromEditor(this.globalStore?.editor, annotationId);\n }\n });\n this.globalStore = { ...this.globalStore, ...store, filteredComments };\n this.highlightCommentsFromGlobalStore();\n }\n catch (error) {\n // handle error\n VeltService.catchError('Error updating global store:', error);\n }\n };\n this.highlightCommentsFromGlobalStore = () => {\n try {\n const { editor, filteredComments } = this.globalStore;\n if (!editor || !filteredComments)\n return;\n for (const annotation of filteredComments) {\n const textToFind = annotation?.context?.textEditorConfig?.text;\n const occurrence = annotation?.context?.textEditorConfig?.occurrence;\n if (textToFind) {\n this.plugin.editorService.highlightTextWithVeltComment(editor, textToFind, {\n annotationId: annotation?.annotationId,\n multiThreadAnnotationId: annotation?.multiThreadAnnotationId,\n occurrence: occurrence,\n targetTextNodeId: annotation?.context?.textEditorConfig?.targetTextNodeId,\n originalAnnotation: annotation, // Pass the complete annotation object\n });\n }\n }\n }\n catch (error) {\n // handle error\n VeltService.catchError('Error highlighting comments from global store:', error);\n }\n };\n this.plugin = plugin;\n try {\n VeltService.subscribeToSelectedCommentsMap(this.plugin.editorId, (selectedCommentsMap) => {\n try {\n this.updateGlobalStore({ selectedCommentsMap });\n }\n catch (error) {\n VeltService.catchError('Error updating global store:', error);\n }\n });\n }\n catch (error) {\n VeltService.catchError('Error subscribing to selected comments map:', error);\n }\n }\n}\nStoreService.storeInstanceMap = new Map();\n","import { EditorService } from './editor';\nimport { StoreService } from './store';\nconst instanceMap = new Map();\nexport class Plugin {\n constructor(editorId, editor) {\n this.editorId = editorId;\n this.editor = editor;\n this.editorService = new EditorService(this);\n if (editor) {\n this.editorService.initializeTransactionListener(editor);\n }\n this.storeService = new StoreService(this);\n }\n static getInstance(config) {\n let { editorId, editor } = config || {};\n if (!editorId) {\n editorId = 'default';\n }\n if (!instanceMap.has(editorId)) {\n instanceMap.set(editorId, new Plugin(editorId, editor));\n }\n return instanceMap.get(editorId);\n }\n}\n","import { CommentNode } from \"./comment-node\";\n/** Use the node’s canonical type string */\nconst COMMENT_TYPE = CommentNode.getType();\n/**\n * export JSON without comment nodes.\n */\nexport function exportJSONWithoutComments(editor) {\n const state = editor.getEditorState();\n return state.read(() => {\n // The runtime JSON (from Lexical) may include `selection`.\n const raw = state.toJSON();\n // 1) Strip comment nodes from the root\n const strippedRoot = stripCommentsFromRoot(raw.root);\n // 2) Normalize adjacent text nodes created by unwrapping\n const normalizedRoot = normalizeTextNodesInRoot(strippedRoot);\n // 3) Return only the shape Lexical expects for SerializedEditorState\n const result = {\n root: normalizedRoot,\n };\n return result;\n });\n}\n/* -------------------- internals (strictly typed) -------------------- */\nfunction stripCommentsFromRoot(root) {\n const nextChildren = stripFromChildrenArray(root.children);\n return {\n ...root,\n children: nextChildren,\n };\n}\n/**\n * Flattens comment nodes inside a children array and recurses.\n */\nfunction stripFromChildrenArray(children) {\n const out = [];\n for (const child of children) {\n if (child.type === COMMENT_TYPE) {\n // Unwrap: push processed grandchildren directly\n if (isElementNode(child)) {\n const flattened = stripFromChildrenArray(child.children);\n out.push(...flattened);\n }\n // If a non-element comment node ever appears, drop it.\n continue;\n }\n out.push(stripCommentsFromNode(child));\n }\n return out;\n}\nfunction stripCommentsFromNode(node) {\n if (isElementNode(node)) {\n const nextChildren = stripFromChildrenArray(node.children);\n return {\n ...node,\n children: nextChildren,\n };\n }\n return node;\n}\n/* -------------------- normalization (strictly typed) -------------------- */\nfunction normalizeTextNodesInRoot(root) {\n const normalizedKids = root.children.map(normalizeTextNodesInNode);\n return {\n ...root,\n children: mergeAdjacentTextNodes(normalizedKids),\n };\n}\nfunction normalizeTextNodesInNode(node) {\n if (isElementNode(node)) {\n const normalizedKids = node.children.map(normalizeTextNodesInNode);\n return {\n ...node,\n children: mergeAdjacentTextNodes(normalizedKids),\n };\n }\n return node;\n}\nfunction mergeAdjacentTextNodes(children) {\n if (children.length < 2)\n return children;\n const merged = [];\n let i = 0;\n while (i < children.length) {\n const cur = children[i];\n if (isTextNode(cur)) {\n let text = cur.text ?? \"\";\n let j = i + 1;\n while (j < children.length) {\n const nxt = children[j];\n if (isTextNode(nxt) && haveSameTextFormat(cur, nxt)) {\n text += nxt.text ?? \"\";\n j++;\n }\n else {\n break;\n }\n }\n merged.push({ ...cur, text });\n i = j;\n }\n else {\n merged.push(cur);\n i++;\n }\n }\n return merged;\n}\n/* -------------------- type guards & equality -------------------- */\nfunction isElementNode(node) {\n // SerializedElementNode always has a 'children' array\n return Object.prototype.hasOwnProperty.call(node, \"children\");\n}\nfunction isTextNode(node) {\n return node.type === \"text\";\n}\nfunction haveSameTextFormat(a, b) {\n return (a.format === b.format &&\n a.style === b.style &&\n a.mode === b.mode &&\n a.detail === b.detail);\n}\n","import { Constants } from \"./constants\";\nimport { EditorService } from \"./editor\";\nimport { Plugin } from \"./plugin\";\nimport { VeltService } from \"./velt\";\nexport * from './comment-node';\nexport * from './serializer';\nconst VELT_SDK_VERSION = '4.5.2-beta.1';\nexport const triggerAddComment = async (editor, config) => {\n try {\n return addComment({ editor, context: config?.context });\n }\n catch (error) {\n VeltService.catchError('Error triggering add comment', error);\n }\n};\n/**\n * Creates a comment annotation for the currently selected text in the editor\n * This function handles the entire flow from getting the selected text to creating\n * the actual comment through the Velt service\n */\nexport const addComment = async ({ editorId, editor, context: clientContext, }) => {\n try {\n // get comment element from Velt sdk\n const commentElement = VeltService.getCommentElement();\n if (!commentElement)\n return;\n // add 500ms delay\n await new Promise(resolve => setTimeout(resolve, 500));\n // Ensure we have an editor ID for proper tracking\n if (!editorId) {\n editorId = EditorService.getEditorId(editor) ?? undefined;\n }\n // Initialize plugin instance with editor context\n const plugin = Plugin.getInstance({ editorId, editor });\n // let context: any = {};\n // if (config?.context && typeof config.context === 'object') {\n // context = config.context;\n // }\n // Get text currently selected by the user\n const selectedText = plugin.editorService.getSelectedText(editor);\n // Exit if no text is selected - nothing to comment on\n if (!selectedText) {\n return;\n }\n const targetTextNode = plugin.editorService.findParentNodeWithId(editor);\n let targetTextNodeId = undefined;\n let locationId = undefined;\n if (targetTextNode) {\n targetTextNodeId = targetTextNode.id;\n locationId = targetTextNode.closest(`[${Constants.ATTRIBUTES.LOCATION_ID}]`)?.getAttribute(Constants.ATTRIBUTES.LOCATION_ID);\n }\n // Calculate which occurrence of the text this selection represents using the new function\n const occurrence = plugin.editorService.findOccurrenceIndex(editor, selectedText, targetTextNode);\n // Exit if we can't determine occurrence information\n if (!occurrence) {\n return;\n }\n // Initialize context object for the comment\n let context = {};\n if (clientContext && typeof clientContext === 'object') {\n context = { ...context, ...clientContext };\n }\n context.textEditorConfig = {\n text: selectedText,\n occurrence: occurrence,\n targetTextNodeId: targetTextNodeId,\n };\n if (editorId) {\n context.textEditorConfig.editorId = editorId;\n }\n let location = undefined;\n if (locationId) {\n location = { id: locationId };\n }\n commentElement?.addManualComment({ context, location })?.then((data) => { });\n }\n catch (error) {\n VeltService.catchError('Error adding lexical-velt-comments', error);\n }\n};\nexport function highlightComments(editor, commentAnnotations, editorId) {\n try {\n renderComments({ editor, editorId, commentAnnotations });\n }\n catch (error) {\n VeltService.catchError('Error highlighting lexical-velt-comments', error);\n }\n}\nexport const renderComments = ({ editor, editorId, commentAnnotations }) => {\n try {\n if (editor) {\n if (!editorId) {\n editorId = EditorService.getEditorId(editor) ?? undefined;\n }\n if (!Array.isArray(commentAnnotations)) {\n return;\n }\n commentAnnotations = JSON.parse(JSON.stringify(commentAnnotations));\n const lexicalCommentAnnotations = commentAnnotations\n ?.filter((annotation) => !!annotation.context?.textEditorConfig &&\n annotation.context?.textEditorConfig?.editorId === editorId) || [];\n const plugin = Plugin.getInstance({ editorId, editor });\n plugin.storeService.updateGlobalStore({ editor, comments: lexicalCommentAnnotations });\n VeltService.subscribeToSelectedAnnotationsMapSingleton();\n }\n }\n catch (error) {\n VeltService.catchError('Error highlighting lexical-velt-comments', error);\n }\n};\n"],"names":["Constants","EDITOR_ID","LOCATION_ID","CommentNode","ElementNode","getType","clone","node","__annotationId","__multiThreadAnnotationId","__key","constructor","annotationId","multiThreadAnnotationId","key","super","this","createDOM","config","dom","document","createElement","setAttribute","updateDOM","prevNode","importJSON","serializedNode","$createCommentNode","exportJSON","type","version","isInline","$isCommentNode","_a","VeltService","selectedCommentsMap","Map","subscribers","isSubscribed","getCommentElement","velt","window","Velt","error","catchError","addManualComment","context","location","commentElement","Promise","resolve","updateContext","subscribeToSelectedCommentsMap","id","subscriber","subscribeToSelectedAnnotationsMapSingleton","set","unsubscribeFromSelectedCommentsMap","delete","message","otherProperties","properties","sdkVersion","getSelectedComments","subscribe","comments","forEach","comment","map1","map2","size","value","get","areMapsEqual","updateSelectedCommentsMap","UtilityService","kmpSearch","text","pattern","startPos","maxOccurrences","positions","length","lps","computeKMPTable","i","j","push","Array","fill","len","collectTextContent","root","nodeMap","combinedText","currentOffset","traverse","depth","$isTextNode","getTextContent","startOffset","endOffset","start","end","$isElementNode","children","getChildren","nodeType","parent","getParent","siblings","indexOf","isParagraphBreak","getTextNodesInRange","fromPos","toPos","excludeAnnotationId","textNodes","item","Math","max","min","isPartial","isRangeAlreadyWrapped","currentPosition","foundWrapping","nodeStart","nodeEnd","child","findParagraphParent","current","getContainerTextContent","containerElement","editor","containerStartOffset","containerEndOffset","nodeKey","getKey","nodeDOMElement","getElementByKey","contains","substring","findCommonParentWithId","node1","node2","editorElement","MAX_ITERATIONS","iterationCount","ancestors1WithId","current1","body","parentElement","current2","includes","findContainerWithId","startNode","getSelectionTextRange","selection","$getRoot","anchorNode","anchor","getNode","focusNode","focus","anchorOffset","offset","focusOffset","anchorTextPos","focusTextPos","wrapPartialTextNode","textNode","commentNode","writableNode","getWritable","fullText","beforeText","selectedText","afterText","selectedTextNode","$createTextNode","setFormat","getFormat","setStyle","getStyle","append","beforeNode","insertBefore","afterNode","remove","wrapEntireTextNode","parentWritable","nodeIndex","getIndexWithinParent","splice","wrapMultipleTextNodesWithFormatting","textNodesToWrap","nodesByParagraph","nodeData","paragraphParent","paragraphKey","has","isFirstParagraph","nodesInParagraph","entries","paragraphCommentNode","wrapMultipleNodesInSameParagraph","sortedNodes","sort","a","b","firstNodeData","lastNodeData","firstNode","insertionIndex","selectedPartNode","lastNode","replace","EditorService","plugin","initializeTransactionListener","editorUpdateListenerRemover","registerUpdateListener","editorState","prevEditorState","hasDocumentChanged","onTransaction","err","destroyTransactionListener","undefined","currentState","prevState","read","currentText","prevText","changedAnnotationContent","annotationsToCheck","collectDocumentAnnotations","data","from","changes","processAnnotationChanges","updateVeltComments","getCommentNodeTextWithStructure","self","getEditorState","prevAnnotationData","storeService","annotationStore","nodeSize","existingAnnotation","pos","nodes","annotation","originalText","textEditorConfig","originalOccurrence","occurrence","targetTextNodeId","Error","isArray","contentChanged","detectContentChanges","targetTextNodeIdChanged","newTargetTextNodeId","searchResults","detectContainerChanges","currentOccurrence","calculateNewOccurrence","occurrenceChanged","createAnnotationChange","map","n","join","params","containerChangeResult","processContainerChanges","changed","newId","findTextInDomElement","findTextInDocument","foundNodePosition","currentPos","editorDOMElement","getRootElement","querySelector","newContainer","findNewContainer","newSearchResults","elementsWithIds","querySelectorAll","element","textContent","annotationRange","result","originalTargetTextNodeId","changeData","JSON","parse","stringify","removeVeltCommentFromEditor","setTimeout","getSelectedText","$getSelection","$isRangeSelection","findOccurrenceIndex","targetContainer","trim","matches","selectionStart","selectionEnd","range","findTextInContainer","findTextInEditor","searchText","containerId","getElementById","containerContent","findParentNodeWithId","anchorDOMNode","focusDOMNode","anchorKey","parentKey","focusKey","fallbackNode","endNode","commonParent","tagName","toLowerCase","containerText","normalizedSelected","highlightTextWithVeltComment","textToFind","commentOptions","desiredOccurrence","storedData","storedConfig","newConfig","originalAnnotation","domNode","locationId","closest","getAttribute","matchToConsider","pluginReference","position","to","setVeltComment","domElementId","update","nodesRemoved","commentNodesToRemove","firstChild","insertAfter","nodeError","getEditorId","_config","namespace","StoreService","globalStore","filteredComments","globalAnnotationStore","getAll","clear","hasAnnotationTextChanged","currentAnnotation","storedText","updateGlobalStore","store","newSelectedCommentsMap","oldFilteredComments","filter","status","newAnnotationIds","Set","editorService","highlightCommentsFromGlobalStore","editorId","storeInstanceMap","instanceMap","Plugin","getInstance","COMMENT_TYPE","stripFromChildrenArray","out","stripCommentsFromNode","isElementNode","flattened","nextChildren","normalizeTextNodesInNode","normalizedKids","mergeAdjacentTextNodes","merged","cur","isTextNode","nxt","haveSameTextFormat","Object","prototype","hasOwnProperty","call","format","style","mode","detail","addComment","async","clientContext","targetTextNode","then","renderComments","commentAnnotations","lexicalCommentAnnotations","state","normalizeTextNodesInRoot","stripCommentsFromRoot","toJSON"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/constants/common.ts","../../src/utils/console.ts","../../src/utils/common.ts","../../src/utils/host.ts","../../src/adapters/host/marks.ts","../../src/adapters/host/doc.ts","../../src/adapters/velt.ts","../../src/core/state.ts","../../src/core/registry.ts","../../src/features/updateContent.ts","../../src/core/extension.ts","../../src/features/commentRenderer.ts","../../src/features/renderComments.ts","../../src/utils/serializer.ts","../../src/features/addComment.ts","../../src/adapters/host/storage.ts"],"sourcesContent":["/**\n * Shared constants for Lexical-Velt Comments integration\n *\n * This module contains all magic strings and configuration constants\n * used across the application. Migrated from src/constants.ts\n */\nexport const Constants = {\n /**\n * DOM attribute names used for editor identification and location tracking.\n */\n ATTRIBUTES: {\n EDITOR_ID: 'data-editor-id',\n LOCATION_ID: 'data-velt-location-id',\n ANNOTATION_ID: 'annotation-id',\n MULTI_THREAD_ANNOTATION_ID: 'multi-thread-annotation-id',\n },\n /**\n * Mark/extension name constant.\n * Generic constant name, value is Lexical-specific for this package.\n */\n MARK_NAME: 'lexicalVeltComments',\n /**\n * Storage key constant.\n * Generic constant name, value is Lexical-specific for this package.\n */\n STORAGE_KEY: 'lexicalVeltComments',\n /**\n * Custom element tag name for comment nodes.\n * CRITICAL: Must be 'velt-comment-text' to match Tiptap/Lexical/Slate implementations.\n */\n TAG_NAME: 'velt-comment-text',\n /**\n * Default extension name for error messages.\n */\n DEFAULT_EXTENSION_NAME: 'VeltComments',\n /**\n * Lexical node type name for comment nodes.\n * Lexical-specific constant for CommentNode type.\n */\n NODE_TYPE: 'comment',\n /**\n * Default editor ID used when no editor ID is provided.\n * Uses '__default__' to avoid collision with user-provided values.\n */\n DEFAULT_EDITOR_ID: '__default__',\n /**\n * Terminal status type for resolved/closed comments.\n */\n STATUS_TERMINAL: 'terminal',\n /**\n * Log prefix for console messages.\n */\n LOG_PREFIX: 'LexicalVeltComments: ',\n /**\n * Session storage key for debug mode.\n */\n DEBUG_MODE_KEY: 'debugMode',\n /**\n * Session storage key for forced debug mode.\n */\n FORCE_DEBUG_MODE_KEY: 'forceDebugMode',\n /**\n * Block-level node types in Lexical that should have newline breaks after them.\n * Used for text content collection and block boundary detection.\n */\n BLOCK_TYPES: ['paragraph', 'heading', 'listitem', 'quote', 'code'],\n};\n","/**\n * Console logging utility with sessionStorage-based debug mode control.\n *\n * This module provides a standardized logging interface that can be enabled/disabled\n * via sessionStorage flags. All console access in the codebase MUST go through this utility.\n *\n * @example\n * ```typescript\n * // Standard logging (only shown when debug mode is enabled)\n * Console.log('Debug message');\n * Console.warn('Warning message');\n * Console.debug('Debug info');\n *\n * // Error handling in try/catch blocks (always shown as warnings)\n * try {\n * // ... code\n * } catch (error) {\n * Console.catch('Operation failed', error);\n * }\n *\n * // Enable debug mode in browser console:\n * sessionStorage.setItem('debugMode', 'true');\n * ```\n */\nimport { Constants } from '@/constants/common';\n/**\n * Console utility class providing controlled logging with debug mode support.\n *\n * Logging methods (log, warn, error, debug, info) are only active when debug mode\n * is enabled via sessionStorage. The `catch` method always outputs as warnings\n * for error handling visibility.\n */\nexport class Console {\n /**\n * Check if debug logging should be shown.\n * Checks sessionStorage for debugMode or forceDebugMode flags.\n *\n * @returns true if debug mode is enabled, false otherwise\n */\n static showLogs() {\n try {\n return !!sessionStorage.getItem(Constants.DEBUG_MODE_KEY) || !!sessionStorage.getItem(Constants.FORCE_DEBUG_MODE_KEY);\n }\n catch (err) {\n Console.catch('Error in showLogs: ', err);\n return false;\n }\n }\n}\n/** Log a message (only when debug mode is enabled via sessionStorage) */\nConsole.log = Console.showLogs() ? console.log : () => { };\n/** Log a warning (only when debug mode is enabled via sessionStorage) */\nConsole.warn = Console.showLogs() ? console.warn : () => { };\n/** Log an error (only when debug mode is enabled via sessionStorage) */\nConsole.error = Console.showLogs() ? console.error : () => { };\n/** Log debug info (only when debug mode is enabled via sessionStorage) */\nConsole.debug = Console.showLogs() ? console.debug : () => { };\n/** Log info (only when debug mode is enabled via sessionStorage) */\nConsole.info = Console.showLogs() ? console.info : () => { };\n/** Flag indicating if logging is enabled (used by catch method) */\nConsole.logsEnabled = true;\n/**\n * Log caught errors from try/catch blocks.\n * Always outputs as console.warn with the LOG_PREFIX for visibility.\n * Use this instead of console.error in catch blocks.\n *\n * @param message - Descriptive message about what operation failed\n * @param error - Optional error object caught in the try/catch\n */\nConsole.catch = (message, error) => {\n try {\n if (Console.logsEnabled) {\n if (error !== undefined) {\n console.warn(Constants.LOG_PREFIX, message, error);\n }\n else {\n console.warn(Constants.LOG_PREFIX, message);\n }\n }\n }\n catch (err) {\n // Silently fail to prevent infinite loops\n }\n};\n","/**\n * Pure utility functions with zero side effects\n *\n * This module contains pure, testable utility functions extracted from src/utility.ts.\n * These functions have NO dependencies on Lexical, Velt, or any external SDKs.\n */\n/**\n * KMP Search implementation for finding text patterns\n *\n * Pure function for efficient string pattern matching using the Knuth-Morris-Pratt algorithm.\n * Migrated from src/utility.ts UtilityService.kmpSearch()\n *\n * @param text - The text to search in\n * @param pattern - The pattern to search for\n * @param startPos - Starting position offset (default: 0)\n * @param maxOccurrences - Maximum number of occurrences to find (optional)\n * @returns Array of positions where pattern was found\n */\nexport const kmpSearch = (text, pattern, startPos = 0, maxOccurrences) => {\n try {\n const positions = [];\n if (!pattern || !text || pattern.length > text.length)\n return positions;\n const lps = computeKMPTable(pattern);\n let i = 0;\n let j = 0;\n while (i < text.length) {\n if (pattern[j] === text[i]) {\n i++;\n j++;\n }\n if (j === pattern.length) {\n positions.push(startPos + i - j);\n j = lps[j - 1];\n if (maxOccurrences && positions.length >= maxOccurrences) {\n break;\n }\n }\n else if (i < text.length && pattern[j] !== text[i]) {\n if (j !== 0) {\n j = lps[j - 1];\n }\n else {\n i++;\n }\n }\n }\n return positions;\n }\n catch (error) {\n return [];\n }\n};\n/**\n * Compute KMP failure function table\n *\n * Pure function that computes the longest proper prefix which is also a suffix (LPS) table\n * for the Knuth-Morris-Pratt algorithm.\n * Migrated from src/utility.ts UtilityService.computeKMPTable()\n *\n * @param pattern - The pattern to compute table for\n * @returns Array representing the failure function (LPS table)\n */\nexport const computeKMPTable = (pattern) => {\n try {\n const lps = new Array(pattern.length).fill(0);\n let len = 0;\n let i = 1;\n while (i < pattern.length) {\n if (pattern[i] === pattern[len]) {\n len++;\n lps[i] = len;\n i++;\n }\n else {\n if (len !== 0) {\n len = lps[len - 1];\n }\n else {\n lps[i] = 0;\n i++;\n }\n }\n }\n return lps;\n }\n catch (error) {\n return [];\n }\n};\n/**\n * Find text occurrences in a string\n *\n * Convenience function that uses KMP search to find all occurrences.\n *\n * @param text - Text to search in\n * @param searchText - Text to search for\n * @returns Array of occurrence matches with start/end positions\n */\nexport const findTextOccurrences = (text, searchText) => {\n const positions = kmpSearch(text, searchText);\n return positions.map((start) => ({\n start,\n end: start + searchText.length,\n }));\n};\n/**\n * Calculate occurrence index for a given position\n *\n * Determines which occurrence (1-indexed) a given position corresponds to.\n *\n * @param matches - Array of match positions with start/end\n * @param position - Position to find occurrence for\n * @returns 1-indexed occurrence number, or null if not found\n */\nexport const calculateOccurrenceIndex = (matches, position) => {\n for (let i = 0; i < matches.length; i++) {\n const match = matches[i];\n if (position >= match.start && position <= match.end) {\n return i + 1; // Return 1-indexed occurrence\n }\n }\n return null;\n};\n","/**\n * Host editor-specific utility functions\n *\n * This module contains host editor-specific utility functions that are shared\n * across multiple adapters. These functions depend on host editor types (Lexical).\n *\n * For pure utilities without host editor dependencies, see utils/common.ts\n */\nimport { $isElementNode, $isTextNode, } from 'lexical';\nimport { Constants } from '@/constants/common';\nimport { Console } from '@/utils/console';\n/**\n * Collect all text content including paragraph breaks.\n * Migrated from src/utility.ts UtilityService.collectTextContent()\n *\n * @param root - Root Lexical node to traverse\n * @returns Object with combined text and node map\n */\nexport const collectTextContent = (root) => {\n try {\n const nodeMap = [];\n let combinedText = '';\n let currentOffset = 0;\n const traverse = (node, depth = 0) => {\n if ($isTextNode(node)) {\n const text = node.getTextContent();\n const startOffset = currentOffset;\n const endOffset = currentOffset + text.length;\n nodeMap.push({\n node,\n start: startOffset,\n end: endOffset,\n });\n combinedText += text;\n currentOffset += text.length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n const nodeType = node.getType();\n for (let i = 0; i < children.length; i++) {\n traverse(children[i], depth + 1);\n }\n // Add newline after any block-level element (not just paragraphs)\n // This ensures text matching works correctly across block boundaries\n if (Constants.BLOCK_TYPES.includes(nodeType) && depth === 1) {\n const parent = node.getParent();\n if (parent && $isElementNode(parent)) {\n const siblings = parent.getChildren();\n const nodeIndex = siblings.indexOf(node);\n if (nodeIndex < siblings.length - 1) {\n const startOffset = currentOffset;\n const endOffset = currentOffset + 1;\n nodeMap.push({\n node: null,\n start: startOffset,\n end: endOffset,\n isParagraphBreak: true,\n });\n combinedText += '\\n';\n currentOffset += 1;\n }\n }\n }\n }\n };\n traverse(root);\n return { combinedText, nodeMap };\n }\n catch (error) {\n Console.catch('Error collecting text content', error);\n return { combinedText: '', nodeMap: [] };\n }\n};\n/**\n * Get text content for a specific container.\n * Migrated from src/utility.ts UtilityService.getContainerTextContent()\n *\n * @param containerElement - DOM element representing the container\n * @param editor - Lexical editor instance\n * @param nodeMap - Node map from collectTextContent\n * @param combinedText - Combined text from collectTextContent\n * @returns Container text content with start offset, or null if not found\n */\nexport const getContainerTextContent = (containerElement, editor, nodeMap, combinedText) => {\n try {\n let containerStartOffset = -1;\n let containerEndOffset = -1;\n for (let i = 0; i < nodeMap.length; i++) {\n const item = nodeMap[i];\n if (item.isParagraphBreak) {\n continue;\n }\n if (item.node) {\n try {\n const nodeKey = item.node.getKey();\n const nodeDOMElement = editor.getElementByKey(nodeKey);\n if (nodeDOMElement && containerElement.contains(nodeDOMElement)) {\n if (containerStartOffset === -1) {\n containerStartOffset = item.start;\n }\n containerEndOffset = item.end;\n }\n }\n catch (error) {\n continue;\n }\n }\n }\n if (containerStartOffset === -1 || containerEndOffset === -1) {\n return null;\n }\n const containerText = combinedText.substring(containerStartOffset, containerEndOffset);\n return {\n text: containerText,\n startOffset: containerStartOffset,\n };\n }\n catch (error) {\n return null;\n }\n};\n","/**\n * Lexical comment marks adapter\n *\n * This module manages visual representation of annotations in the editor.\n * It handles creating, updating, and removing CommentNodes (Lexical's equivalent of marks).\n *\n * CRITICAL: This is ONE of ONLY TWO places where Lexical types may be imported.\n * The other is types/host.ts.\n *\n * Migrated from src/editor.ts EditorService comment node operations.\n */\nimport { $createTextNode, $getRoot, $getSelection, $isElementNode, $isTextNode, $setSelection, ElementNode, } from 'lexical';\nimport { Constants } from '@/constants/common';\nimport { Console } from '@/utils/console';\nimport { kmpSearch } from '@/utils/common';\nimport { collectTextContent, getContainerTextContent } from '@/utils/host';\nexport class CommentNode extends ElementNode {\n static getType() {\n return Constants.NODE_TYPE;\n }\n static clone(node) {\n const cloned = new CommentNode(node.__annotationId, node.__multiThreadAnnotationId, node.__key);\n return cloned;\n }\n constructor(annotationId, multiThreadAnnotationId, key) {\n super(key);\n this.__annotationId = annotationId;\n this.__multiThreadAnnotationId = multiThreadAnnotationId;\n }\n createDOM(config) {\n const dom = document.createElement(Constants.TAG_NAME);\n if (this.__annotationId) {\n dom.setAttribute(Constants.ATTRIBUTES.ANNOTATION_ID, this.__annotationId);\n }\n if (this.__multiThreadAnnotationId) {\n dom.setAttribute(Constants.ATTRIBUTES.MULTI_THREAD_ANNOTATION_ID, this.__multiThreadAnnotationId);\n }\n return dom;\n }\n updateDOM(_prevNode, _dom, _config) {\n return false;\n }\n static importJSON(serializedNode) {\n return $createCommentNode(serializedNode.annotationId, serializedNode.multiThreadAnnotationId);\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n annotationId: this.__annotationId,\n multiThreadAnnotationId: this.__multiThreadAnnotationId,\n type: Constants.NODE_TYPE,\n version: 1,\n };\n }\n isInline() {\n return true;\n }\n}\nexport const $createCommentNode = (annotationId, multiThreadAnnotationId) => {\n return new CommentNode(annotationId, multiThreadAnnotationId);\n};\nexport const $isCommentNode = (node) => {\n return node instanceof CommentNode;\n};\n/**\n * Check if a text range is already wrapped.\n * Migrated from src/utility.ts UtilityService.isRangeAlreadyWrapped()\n */\nconst isRangeAlreadyWrapped = (root, fromPos, toPos, annotationId) => {\n try {\n let currentPosition = 0;\n let foundWrapping = false;\n const traverse = (node) => {\n if ($isCommentNode(node)) {\n if (node.__annotationId === annotationId) {\n const text = node.getTextContent();\n const nodeStart = currentPosition;\n const nodeEnd = currentPosition + text.length;\n if (nodeStart <= fromPos && nodeEnd >= toPos) {\n foundWrapping = true;\n return true;\n }\n }\n const text = node.getTextContent();\n currentPosition += text.length;\n }\n else if ($isTextNode(node)) {\n const text = node.getTextContent();\n currentPosition += text.length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n if (traverse(child)) {\n return true;\n }\n }\n const nodeType = node.getType();\n if (nodeType === 'paragraph') {\n const parent = node.getParent();\n if (parent && $isElementNode(parent)) {\n const siblings = parent.getChildren();\n const nodeIndex = siblings.indexOf(node);\n if (nodeIndex < siblings.length - 1) {\n currentPosition += 1;\n }\n }\n }\n }\n return false;\n };\n traverse(root);\n return foundWrapping;\n }\n catch (error) {\n return true;\n }\n};\n/**\n * Get text nodes within a specific character range.\n * Migrated from src/utility.ts UtilityService.getTextNodesInRange()\n */\nconst getTextNodesInRange = (root, fromPos, toPos, excludeAnnotationId) => {\n try {\n const textNodes = [];\n const { combinedText, nodeMap } = collectTextContent(root);\n for (const item of nodeMap) {\n if (item.isParagraphBreak || !item.node) {\n continue;\n }\n const parent = item.node.getParent();\n if ($isCommentNode(parent) && excludeAnnotationId && parent.__annotationId === excludeAnnotationId) {\n continue;\n }\n if (item.end > fromPos && item.start < toPos) {\n const startOffset = Math.max(0, fromPos - item.start);\n const endOffset = Math.min(item.node.getTextContent().length, toPos - item.start);\n const isPartial = startOffset > 0 || endOffset < item.node.getTextContent().length;\n textNodes.push({\n node: item.node,\n startOffset,\n endOffset,\n isPartial\n });\n }\n }\n return textNodes;\n }\n catch (error) {\n Console.catch('Error getting text nodes in range', error);\n return [];\n }\n};\n/**\n * Find the block-level parent of a text node.\n * This could be a paragraph, heading, list-item, or any other block-level element.\n * Migrated from src/utility.ts UtilityService.findParagraphParent()\n */\nconst findParagraphParent = (node) => {\n // Block-level node types in Lexical\n const blockTypes = ['paragraph', 'heading', 'listitem', 'quote', 'code'];\n let current = node.getParent();\n while (current) {\n if ($isElementNode(current)) {\n const nodeType = current.getType();\n // Check if it's a known block type, or if it's a direct child of root\n if (blockTypes.includes(nodeType)) {\n return current;\n }\n // Also handle custom block-level elements by checking if parent is root\n const parent = current.getParent();\n if (parent && parent.getType() === 'root') {\n return current;\n }\n }\n current = current.getParent();\n }\n return null;\n};\n/**\n * Wrap a partial text node (when selection is within the node) - PRESERVING FORMATTING\n * Migrated from src/utility.ts UtilityService.wrapPartialTextNode()\n */\nconst wrapPartialTextNode = (textNode, startOffset, endOffset, commentNode) => {\n try {\n // Get info from original node BEFORE any getWritable() calls\n const fullText = textNode.getTextContent();\n const format = textNode.getFormat();\n const style = textNode.getStyle();\n const parent = textNode.getParent();\n const nodeIndex = textNode.getIndexWithinParent();\n if (!parent)\n return;\n const beforeText = fullText.substring(0, startOffset);\n const selectedText = fullText.substring(startOffset, endOffset);\n const afterText = fullText.substring(endOffset);\n // Create the selected portion as a text node with the same formatting\n const selectedTextNode = $createTextNode(selectedText);\n selectedTextNode.setFormat(format);\n selectedTextNode.setStyle(style);\n commentNode.append(selectedTextNode);\n // Build array of nodes to insert\n const nodesToInsert = [];\n if (beforeText) {\n const beforeNode = $createTextNode(beforeText);\n beforeNode.setFormat(format);\n beforeNode.setStyle(style);\n nodesToInsert.push(beforeNode);\n }\n nodesToInsert.push(commentNode);\n if (afterText) {\n const afterNode = $createTextNode(afterText);\n afterNode.setFormat(format);\n afterNode.setStyle(style);\n nodesToInsert.push(afterNode);\n }\n // Now get writable parent and perform the splice\n const parentWritable = parent.getWritable();\n parentWritable.splice(nodeIndex, 1, nodesToInsert);\n }\n catch (error) {\n Console.catch('Error in wrapPartialTextNode', error);\n }\n};\n/**\n * Wrap an entire text node - PRESERVING FORMATTING\n * Migrated from src/utility.ts UtilityService.wrapEntireTextNode()\n */\nconst wrapEntireTextNode = (textNode, commentNode) => {\n try {\n // IMPORTANT: Get parent and index BEFORE any mutations\n const parent = textNode.getParent();\n const nodeIndex = textNode.getIndexWithinParent();\n if (!parent) {\n Console.warn('[wrapEntireTextNode] No parent found for text node');\n return;\n }\n // IMPORTANT: First splice to replace the text node with commentNode,\n // THEN append the text node into commentNode\n const parentWritable = parent.getWritable();\n parentWritable.splice(nodeIndex, 1, [commentNode]);\n const writableNode = textNode.getWritable();\n commentNode.append(writableNode);\n }\n catch (error) {\n Console.catch('[wrapEntireTextNode] Error', error);\n }\n};\n/**\n * Wrap multiple text nodes while preserving formatting and paragraph structure.\n * Migrated from src/utility.ts UtilityService.wrapMultipleTextNodesWithFormatting()\n */\nconst wrapMultipleTextNodesWithFormatting = (textNodesToWrap, commentNode) => {\n const logPrefix = `[wrapMultipleTextNodesWithFormatting][${commentNode.__annotationId}]`;\n try {\n const nodesByParagraph = new Map();\n for (const nodeData of textNodesToWrap) {\n const paragraphParent = findParagraphParent(nodeData.node);\n const paragraphKey = paragraphParent ? paragraphParent.getKey() : 'no-paragraph';\n if (!nodesByParagraph.has(paragraphKey)) {\n nodesByParagraph.set(paragraphKey, []);\n }\n nodesByParagraph.get(paragraphKey).push(nodeData);\n }\n let isFirstParagraph = true;\n let paragraphIndex = 0;\n for (const [paragraphKey, nodesInParagraph] of nodesByParagraph.entries()) {\n const paragraphCommentNode = isFirstParagraph\n ? commentNode\n : $createCommentNode(commentNode.__annotationId, commentNode.__multiThreadAnnotationId);\n if (nodesInParagraph.length === 1) {\n const nodeData = nodesInParagraph[0];\n const { node, startOffset, endOffset, isPartial } = nodeData;\n if (isPartial) {\n wrapPartialTextNode(node, startOffset, endOffset, paragraphCommentNode);\n }\n else {\n wrapEntireTextNode(node, paragraphCommentNode);\n }\n }\n else {\n wrapMultipleNodesInSameParagraph(nodesInParagraph, paragraphCommentNode);\n }\n isFirstParagraph = false;\n paragraphIndex++;\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error in wrapMultipleTextNodesWithFormatting`, error);\n }\n};\n/**\n * Wrap multiple text nodes within the same paragraph.\n * Migrated from src/utility.ts UtilityService.wrapMultipleNodesInSameParagraph()\n */\nconst wrapMultipleNodesInSameParagraph = (nodesInParagraph, commentNode) => {\n const logPrefix = `[wrapMultipleNodesInSameParagraph][${commentNode.__annotationId}]`;\n try {\n const sortedNodes = [...nodesInParagraph].sort((a, b) => {\n const aIndex = a.node.getIndexWithinParent();\n const bIndex = b.node.getIndexWithinParent();\n return aIndex - bIndex;\n });\n const firstNodeData = sortedNodes[0];\n const lastNodeData = sortedNodes[sortedNodes.length - 1];\n // IMPORTANT: Get parent and index BEFORE calling getWritable() to avoid stale references\n const originalParent = firstNodeData.node.getParent();\n const originalIndex = firstNodeData.node.getIndexWithinParent();\n if (!originalParent) {\n return;\n }\n // Validate index before proceeding\n const parentChildrenCount = originalParent.getChildrenSize();\n if (originalIndex < 0 || originalIndex >= parentChildrenCount) {\n Console.warn(`${logPrefix} Invalid index ${originalIndex} for parent with ${parentChildrenCount} children`);\n return;\n }\n const firstNode = firstNodeData.node.getWritable();\n const parentWritable = originalParent.getWritable();\n const insertionIndex = originalIndex;\n if (firstNodeData.isPartial && firstNodeData.startOffset > 0) {\n const beforeText = firstNodeData.node.getTextContent().substring(0, firstNodeData.startOffset);\n const selectedText = firstNodeData.node.getTextContent().substring(firstNodeData.startOffset);\n const beforeNode = $createTextNode(beforeText);\n beforeNode.setFormat(firstNode.getFormat());\n beforeNode.setStyle(firstNode.getStyle());\n const selectedPartNode = $createTextNode(selectedText);\n selectedPartNode.setFormat(firstNode.getFormat());\n selectedPartNode.setStyle(firstNode.getStyle());\n // IMPORTANT: First do the splice to replace the original node, THEN append to commentNode\n // This avoids the issue where appending first removes the node from parent\n commentNode.append(selectedPartNode);\n // Verify we can splice before doing it\n const currentSize = parentWritable.getChildrenSize();\n if (insertionIndex + 1 > currentSize) {\n Console.warn(`${logPrefix} Cannot splice: index ${insertionIndex} + 1 > size ${currentSize}`);\n return;\n }\n parentWritable.splice(insertionIndex, 1, [beforeNode, commentNode]);\n }\n else {\n // Verify we can splice before doing it\n const currentSize = parentWritable.getChildrenSize();\n if (insertionIndex + 1 > currentSize) {\n Console.warn(`${logPrefix} Cannot splice: index ${insertionIndex} + 1 > size ${currentSize}`);\n return;\n }\n // IMPORTANT: First do the splice to replace the original node with commentNode,\n // THEN move the original node into commentNode. This prevents the parent from\n // having 0 children when we try to splice.\n parentWritable.splice(insertionIndex, 1, [commentNode]);\n commentNode.append(firstNode);\n }\n for (let i = 1; i < sortedNodes.length - 1; i++) {\n const nodeData = sortedNodes[i];\n const writableNode = nodeData.node.getWritable();\n commentNode.append(writableNode);\n }\n if (sortedNodes.length > 1) {\n const lastNode = lastNodeData.node.getWritable();\n if (lastNodeData.isPartial && lastNodeData.endOffset < lastNodeData.node.getTextContent().length) {\n const selectedText = lastNodeData.node.getTextContent().substring(0, lastNodeData.endOffset);\n const afterText = lastNodeData.node.getTextContent().substring(lastNodeData.endOffset);\n const selectedPartNode = $createTextNode(selectedText);\n selectedPartNode.setFormat(lastNode.getFormat());\n selectedPartNode.setStyle(lastNode.getStyle());\n const afterNode = $createTextNode(afterText);\n afterNode.setFormat(lastNode.getFormat());\n afterNode.setStyle(lastNode.getStyle());\n commentNode.append(selectedPartNode);\n lastNode.replace(afterNode);\n }\n else {\n commentNode.append(lastNode);\n }\n }\n }\n catch (error) {\n Console.catch('Error in wrapMultipleNodesInSameParagraph', error);\n }\n};\n/**\n * Find text matches for an annotation in the editor\n */\nconst findTextMatches = (editor, annotation) => {\n try {\n const matches = [];\n const text = annotation.context?.textEditorConfig?.text;\n if (!text) {\n return matches;\n }\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = collectTextContent(root);\n // Use KMP search to find all occurrences\n const positions = kmpSearch(combinedText, text);\n // Convert to match objects\n for (const pos of positions) {\n matches.push({\n start: pos,\n end: pos + text.length,\n });\n }\n });\n return matches;\n }\n catch (error) {\n Console.catch('Error finding text matches', error);\n return [];\n }\n};\n/**\n * Applies an annotation mark to the editor document at the specified position.\n * This is a unified adapter function that matches TipTap's signature.\n *\n * @param editor The editor instance (typed as unknown to avoid importing editor types in feature modules)\n * @param annotationId The annotation ID to mark\n * @param multiThreadAnnotationId Optional multi-thread annotation ID\n * @param from Start position of the mark\n * @param to End position of the mark\n * @returns True if mark was successfully applied, false otherwise\n */\nexport const applyAnnotationMark = (editor, annotationId, multiThreadAnnotationId, from, to, occurrence = 1) => {\n const logPrefix = '[applyAnnotationMark]';\n try {\n if (!editor || !annotationId) {\n return false;\n }\n if (from >= to) {\n Console.warn(`${logPrefix} Invalid position range: from must be less than to`);\n return false;\n }\n const lexicalEditor = editor;\n // Extract text from the editor at the given positions\n let text = '';\n try {\n lexicalEditor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = collectTextContent(root);\n text = combinedText.substring(from, to);\n });\n }\n catch (error) {\n Console.catch(`${logPrefix} Could not extract text from positions, mark may not apply correctly`, error);\n return false;\n }\n if (!text || text.trim().length === 0) {\n Console.warn(`${logPrefix} No text found at positions ${from}-${to}`);\n return false;\n }\n // Create AnnotationData format and call applyAnnotationMarks\n // Use the provided occurrence (from selection context) instead of hardcoded 1\n const annotationData = [{\n annotationId,\n multiThreadAnnotationId,\n context: {\n textEditorConfig: {\n text,\n occurrence,\n editorId: '', // Will be set by applyAnnotationMarks if needed\n }\n },\n position: {\n from,\n to,\n },\n }];\n // Call the plural version which handles the actual mark application\n applyAnnotationMarks(lexicalEditor, annotationData);\n return true;\n }\n catch (error) {\n Console.catch(`${logPrefix} Error applying annotation mark`, error);\n return false;\n }\n};\n/**\n * Apply comment marks (CommentNodes) for a set of annotations\n *\n * @param editor - Lexical editor instance\n * @param annotations - Annotations to render as comment marks\n */\nexport const applyAnnotationMarks = (editor, annotations) => {\n const logPrefix = '[applyAnnotationMarks]';\n try {\n for (const annotation of annotations) {\n const annotationLogPrefix = `${logPrefix}[${annotation.annotationId}]`;\n const text = annotation.context?.textEditorConfig?.text;\n if (!text || text.length === 0) {\n continue;\n }\n const desiredOccurrence = annotation.context?.textEditorConfig?.occurrence || 1;\n const targetTextNodeId = annotation.context?.textEditorConfig?.targetTextNodeId;\n // Find matches - either in container or entire document\n let matches = [];\n if (targetTextNodeId) {\n // Find matches in specific container\n const domNode = document.getElementById(targetTextNodeId);\n if (domNode) {\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText, nodeMap } = collectTextContent(root);\n const containerContent = getContainerTextContent(domNode, editor, nodeMap, combinedText);\n if (containerContent) {\n const positions = kmpSearch(containerContent.text, text, 0, desiredOccurrence);\n matches = positions.map((pos) => ({\n start: containerContent.startOffset + pos,\n end: containerContent.startOffset + pos + text.length,\n }));\n }\n });\n }\n }\n else {\n // Find matches in entire document\n matches = findTextMatches(editor, annotation);\n }\n if (matches.length === 0) {\n continue;\n }\n const occurrence = Math.min(desiredOccurrence, matches.length);\n const match = matches[occurrence - 1];\n if (!match) {\n continue;\n }\n // Check if already wrapped\n const isAlreadyWrapped = editor.getEditorState().read(() => {\n const root = $getRoot();\n return isRangeAlreadyWrapped(root, match.start, match.end, annotation.annotationId);\n });\n if (isAlreadyWrapped) {\n continue;\n }\n // Apply comment mark (CommentNode)\n try {\n editor.update(() => {\n const root = $getRoot();\n // Clear any existing selection to prevent selection anchor issues during node manipulation\n // This prevents \"selection has been lost\" errors when nodes are replaced\n const selection = $getSelection();\n if (selection) {\n $setSelection(null);\n }\n const textNodesToWrap = getTextNodesInRange(root, match.start, match.end, annotation.annotationId);\n if (textNodesToWrap.length === 0) {\n return;\n }\n const commentNode = $createCommentNode(annotation.annotationId, annotation.multiThreadAnnotationId);\n // Handle different scenarios\n if (textNodesToWrap.length === 1) {\n const { node, startOffset, endOffset, isPartial } = textNodesToWrap[0];\n if (isPartial) {\n wrapPartialTextNode(node, startOffset, endOffset, commentNode);\n }\n else {\n wrapEntireTextNode(node, commentNode);\n }\n }\n else {\n wrapMultipleTextNodesWithFormatting(textNodesToWrap, commentNode);\n }\n }, { discrete: true });\n }\n catch (updateError) {\n Console.catch(`${annotationLogPrefix} Error in editor.update`, updateError);\n throw updateError;\n }\n }\n }\n catch (error) {\n Console.catch('Error applying annotation marks', error);\n }\n};\n/**\n * Remove comment marks (CommentNodes) for specific annotation IDs\n *\n * @param editor - Lexical editor instance\n * @param annotationIds - Array of annotation IDs to remove\n */\nexport const removeAnnotationMarks = (editor, annotationIds) => {\n try {\n editor.update(() => {\n const root = $getRoot();\n // Clear any existing selection to prevent selection anchor issues during node manipulation\n const selection = $getSelection();\n if (selection) {\n $setSelection(null);\n }\n const commentNodesToRemove = [];\n // Traverse to find comment nodes with matching IDs\n function traverse(node) {\n if ($isCommentNode(node)) {\n if (node.__annotationId && annotationIds.includes(node.__annotationId)) {\n commentNodesToRemove.push(node);\n }\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n }\n traverse(root);\n // Remove found comment nodes\n for (const commentNode of commentNodesToRemove) {\n try {\n const writableNode = commentNode.getWritable();\n const children = writableNode.getChildren();\n if (children.length > 0) {\n // Replace comment node with its children\n const firstChild = children[0];\n writableNode.replace(firstChild);\n // Insert remaining children after the first one\n let insertAfter = firstChild;\n for (let i = 1; i < children.length; i++) {\n insertAfter.insertAfter(children[i]);\n insertAfter = children[i];\n }\n }\n else {\n // If no children, check for text content\n const textContent = writableNode.getTextContent();\n if (textContent) {\n const textNode = $createTextNode(textContent);\n writableNode.replace(textNode);\n }\n else {\n writableNode.remove();\n }\n }\n }\n catch (nodeError) {\n Console.catch('Error removing individual comment mark', nodeError);\n // Continue with other nodes\n }\n }\n }, { discrete: true });\n }\n catch (error) {\n Console.catch('Error removing annotation marks', error);\n }\n};\n/**\n * Update comment marks efficiently\n * Uses diff-based updates to minimize DOM changes\n *\n * @param editor - Lexical editor instance\n * @param annotations - Updated annotations\n */\nexport const updateAnnotationMarks = (editor, annotations) => {\n try {\n // Get current annotation IDs in editor\n const currentAnnotationIds = new Set();\n editor.getEditorState().read(() => {\n const root = $getRoot();\n function traverse(node) {\n if ($isCommentNode(node) && node.__annotationId) {\n currentAnnotationIds.add(node.__annotationId);\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n }\n traverse(root);\n });\n // Find annotations to remove (in current but not in new)\n const newAnnotationIds = new Set(annotations.map((a) => a.annotationId));\n const toRemove = Array.from(currentAnnotationIds).filter((id) => !newAnnotationIds.has(id));\n // Remove old annotations\n if (toRemove.length > 0) {\n removeAnnotationMarks(editor, toRemove);\n }\n // Apply new/updated annotations\n applyAnnotationMarks(editor, annotations);\n }\n catch (error) {\n Console.catch('Error updating annotation marks', error);\n }\n};\n/**\n * Clear all annotation comment marks from editor\n *\n * @param editor - Lexical editor instance\n */\nexport const clearAllAnnotationMarks = (editor) => {\n try {\n editor.update(() => {\n const root = $getRoot();\n const commentNodesToRemove = [];\n // Find all comment nodes\n function traverse(node) {\n if ($isCommentNode(node)) {\n commentNodesToRemove.push(node);\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n }\n traverse(root);\n // Remove all comment nodes\n for (const commentNode of commentNodesToRemove) {\n try {\n const writableNode = commentNode.getWritable();\n const children = writableNode.getChildren();\n if (children.length > 0) {\n const firstChild = children[0];\n writableNode.replace(firstChild);\n let insertAfter = firstChild;\n for (let i = 1; i < children.length; i++) {\n insertAfter.insertAfter(children[i]);\n insertAfter = children[i];\n }\n }\n else {\n const textContent = writableNode.getTextContent();\n if (textContent) {\n const textNode = $createTextNode(textContent);\n writableNode.replace(textNode);\n }\n else {\n writableNode.remove();\n }\n }\n }\n catch (nodeError) {\n Console.catch('Error removing comment mark', nodeError);\n }\n }\n });\n }\n catch (error) {\n Console.catch('Error clearing annotation marks', error);\n }\n};\n/**\n * Updates marks based on annotation changes.\n * Currently a no-op: mark updates are handled through applyAnnotationMarks\n * called from handleContentUpdate after state is updated.\n *\n * @param editor The Lexical editor instance\n * @param changes Map of annotation ID to AnnotationChange\n */\nexport const updateMarks = (editor, changes) => {\n // No-op: In Lexical, mark re-rendering is handled through updateAnnotationMarks\n // which is called from handleContentUpdate after state is updated.\n // This function exists for API compatibility with the adapter interface.\n};\n","/**\n * Lexical document adapter\n *\n * This module bridges Lexical's document model with the core system's needs.\n * It handles selection extraction, occurrence finding, and document change tracking.\n *\n * CRITICAL: This is ONE of ONLY TWO places where Lexical types may be imported.\n * The other is types/host.ts.\n *\n * Migrated from src/editor.ts EditorService selection and occurrence logic.\n */\nimport { $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, } from 'lexical';\nimport { Constants } from '@/constants/common';\nimport { Console } from '@/utils/console';\nimport { kmpSearch } from '@/utils/common';\nimport { collectTextContent, getContainerTextContent } from '@/utils/host';\n// Import CommentNode utilities from marks.ts (where CommentNode is defined)\nimport { $isCommentNode } from '@/adapters/host/marks';\n/**\n * Get the text range of current selection, accounting for paragraph breaks.\n * Migrated from src/utility.ts UtilityService.getSelectionTextRange()\n */\nconst getSelectionTextRange = (editor, selection) => {\n const root = $getRoot();\n const { nodeMap } = collectTextContent(root);\n const anchorNode = selection.anchor.getNode();\n const focusNode = selection.focus.getNode();\n const anchorOffset = selection.anchor.offset;\n const focusOffset = selection.focus.offset;\n let anchorTextPos = 0;\n let focusTextPos = 0;\n for (const item of nodeMap) {\n if (item.isParagraphBreak || !item.node) {\n continue;\n }\n const nodeKey = item.node.getKey();\n if (nodeKey === anchorNode.getKey()) {\n anchorTextPos = item.start + anchorOffset;\n }\n if (nodeKey === focusNode.getKey()) {\n focusTextPos = item.start + focusOffset;\n }\n }\n return {\n start: Math.min(anchorTextPos, focusTextPos),\n end: Math.max(anchorTextPos, focusTextPos)\n };\n};\n/**\n * Get current selection context from editor\n *\n * @param editor - Lexical editor instance\n * @returns SelectionContext with text and position info, or null if no selection\n */\nexport const getCurrentSelectionContext = (editor) => {\n const logPrefix = '[getCurrentSelectionContext]';\n try {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return null;\n }\n const text = selection.getTextContent();\n if (!text.trim()) {\n return null;\n }\n // Get selection range using utility function\n const range = getSelectionTextRange(editor, selection);\n const anchorNode = selection.anchor.getNode();\n const focusNode = selection.focus.getNode();\n // Find parent node with ID (targetTextNodeId)\n let targetTextNodeId;\n let locationId;\n try {\n const rootElement = editor.getRootElement();\n if (rootElement) {\n // Get DOM node for anchor position\n const anchorKey = selection.anchor.key;\n const anchorDOMNode = editor.getElementByKey(anchorKey);\n if (anchorDOMNode) {\n // Find parent element with ID\n let currentNode = anchorDOMNode;\n const MAX_ITERATIONS = 100;\n let iterationCount = 0;\n while (currentNode && currentNode !== document.body && iterationCount < MAX_ITERATIONS) {\n if (!rootElement.contains(currentNode)) {\n break;\n }\n if (currentNode.id) {\n targetTextNodeId = currentNode.id;\n break;\n }\n currentNode = currentNode.parentElement;\n iterationCount++;\n }\n // Find location ID from DOM\n if (targetTextNodeId) {\n const locationElement = anchorDOMNode.closest(`[${Constants.ATTRIBUTES.LOCATION_ID}]`);\n locationId = locationElement?.getAttribute(Constants.ATTRIBUTES.LOCATION_ID) || undefined;\n }\n }\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error finding targetTextNodeId or locationId`, error);\n }\n // Calculate occurrence index\n let occurrence = 1;\n try {\n const matches = findOccurrencesByText(editor, text);\n // Find which occurrence matches the current selection\n for (const match of matches) {\n if (match.start === range.start && match.end === range.end) {\n occurrence = match.occurrence;\n break;\n }\n }\n // If no exact match found, find closest occurrence\n if (occurrence === 1 && matches.length > 0) {\n for (const match of matches) {\n if (range.start >= match.start && range.end <= match.end) {\n occurrence = match.occurrence;\n break;\n }\n }\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error calculating occurrence`, error);\n }\n return {\n text,\n from: range.start,\n to: range.end,\n occurrence,\n targetTextNodeId,\n locationId,\n };\n });\n }\n catch (error) {\n Console.catch(`${logPrefix} Error getting selection context`, error);\n return null;\n }\n};\n/**\n * Find all occurrences of text in document (by text only)\n * Helper function that doesn't require full SelectionContext\n *\n * @param editor - Lexical editor instance\n * @param text - Text to search for\n * @returns Array of occurrence matches\n */\nexport const findOccurrencesByText = (editor, text) => {\n try {\n const matches = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = collectTextContent(root);\n // Use KMP search to find all occurrences\n const positions = kmpSearch(combinedText, text);\n // Convert positions to OccurrenceMatch objects\n for (let i = 0; i < positions.length; i++) {\n const pos = positions[i];\n matches.push({\n start: pos,\n end: pos + text.length,\n occurrence: i + 1, // 1-indexed\n });\n }\n });\n return matches;\n }\n catch (error) {\n Console.catch('Error finding occurrences by text', error);\n return [];\n }\n};\n/**\n * Find all occurrences of selected text in document\n *\n * @param editor - Lexical editor instance\n * @param context - Selection context to find occurrences for\n * @returns Array of occurrence matches\n */\nexport const findOccurrences = (editor, context) => {\n try {\n const matches = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = collectTextContent(root);\n // Use KMP search to find all occurrences\n const positions = kmpSearch(combinedText, context.text);\n // Convert positions to OccurrenceMatch objects\n for (let i = 0; i < positions.length; i++) {\n const pos = positions[i];\n matches.push({\n start: pos,\n end: pos + context.text.length,\n occurrence: i + 1, // 1-indexed\n });\n }\n });\n return matches;\n }\n catch (error) {\n Console.catch('Error finding occurrences', error);\n return [];\n }\n};\n/**\n * Check if document content has changed between two editor states\n */\nconst hasDocumentChanged = (currentState, prevState) => {\n try {\n return currentState.read(() => {\n const currentText = $getRoot().getTextContent();\n return prevState.read(() => {\n const prevText = $getRoot().getTextContent();\n return currentText !== prevText;\n });\n });\n }\n catch (error) {\n Console.catch('[hasDocumentChanged] Error comparing editor states', error);\n return true; // Assume changed if we can't determine\n }\n};\n/**\n * Subscribe to document changes\n *\n * @param editor - Lexical editor instance\n * @param callback - Function called when document changes\n * @returns Unsubscribe function\n */\nexport const subscribeToDocumentChanges = (editor, callback) => {\n const logPrefix = '[subscribeToDocumentChanges]';\n try {\n let prevText = '';\n // Initialize previous text\n editor.getEditorState().read(() => {\n prevText = $getRoot().getTextContent();\n });\n // Register update listener\n const unsubscribe = editor.registerUpdateListener(({ editorState, prevEditorState }) => {\n const updateLogPrefix = `${logPrefix}[updateListener]`;\n try {\n // Only process if content actually changed\n if (editorState === prevEditorState) {\n return;\n }\n const docChanged = hasDocumentChanged(editorState, prevEditorState);\n if (!docChanged) {\n return;\n }\n // Calculate change event\n editorState.read(() => {\n const currentText = $getRoot().getTextContent();\n // Simple change detection - in a full implementation, we'd use Lexical's diffing\n // For now, we'll emit a generic change event\n // TODO: Implement more sophisticated change detection using Lexical's transaction system\n const event = {\n type: 'replace',\n from: 0,\n to: prevText.length,\n insertedText: currentText,\n deletedText: prevText,\n };\n callback(event);\n prevText = currentText;\n });\n }\n catch (error) {\n Console.catch(`${updateLogPrefix} Error in document change listener`, error);\n }\n });\n return unsubscribe;\n }\n catch (error) {\n Console.catch(`${logPrefix} Error subscribing to document changes`, error);\n return () => { }; // Return no-op unsubscribe function\n }\n};\n/**\n * Get document text content\n *\n * @param editor - Lexical editor instance\n * @returns Full document text\n */\nexport const getDocumentText = (editor) => {\n try {\n return editor.getEditorState().read(() => {\n return $getRoot().getTextContent();\n });\n }\n catch (error) {\n Console.catch('Error getting document text', error);\n return '';\n }\n};\n/**\n * Map position through document changes\n *\n * @param oldPos - Original position\n * @param changes - Document change event\n * @returns New position or null if position no longer exists\n */\nexport const mapPositionThroughChanges = (oldPos, changes) => {\n try {\n const { type, from, to, insertedText = '', deletedText = '' } = changes;\n // If change is before the position, position is unaffected\n if (to <= oldPos) {\n const changeLength = insertedText.length - deletedText.length;\n return oldPos + changeLength;\n }\n // If change starts before position but ends after, position is within changed region\n if (from < oldPos && to > oldPos) {\n // Position is within deleted region - map to start of inserted text\n return from + insertedText.length;\n }\n // If change is after position, position is unaffected\n if (from >= oldPos) {\n return oldPos;\n }\n return oldPos;\n }\n catch (error) {\n Console.catch('Error mapping position through changes', error);\n return null;\n }\n};\n/**\n * Get editor ID from Lexical editor instance\n *\n * Checks editor config namespace first, then falls back to DOM attributes.\n *\n * @param editor - Lexical editor instance\n * @returns Editor ID string or null if not found\n */\nexport const getEditorId = (editor) => {\n try {\n if (!editor) {\n return null;\n }\n // Check editor config namespace first (Lexical's way)\n if (editor._config?.namespace) {\n return editor._config.namespace;\n }\n // Fall back to DOM attributes (matching Tiptap pattern)\n const rootElement = editor.getRootElement();\n if (rootElement) {\n const editorId = rootElement\n .closest(`[${Constants.ATTRIBUTES.EDITOR_ID}]`)\n ?.getAttribute(Constants.ATTRIBUTES.EDITOR_ID);\n if (editorId) {\n return editorId;\n }\n }\n return null;\n }\n catch (error) {\n Console.catch('Error finding editor ID', error);\n return null;\n }\n};\n/**\n * Resets the editor selection by focusing the editor.\n * Uses Lexical's focus API to reset selection/focus after comment operations.\n *\n * @param editor The editor instance (typed as unknown to avoid importing editor types in feature modules)\n * @returns void\n *\n * @remarks\n * - This function abstracts editor-specific selection reset logic\n * - For Lexical, uses the focus API: `editor.focus()`\n * - Fails silently if focus API is unavailable\n */\nexport const resetEditorSelection = (editor) => {\n const logPrefix = '[resetEditorSelection]';\n try {\n // Type assertion for Lexical focus API\n const editorWithFocus = editor;\n if (editorWithFocus?.focus) {\n editorWithFocus.focus();\n }\n else {\n Console.warn(`${logPrefix} Editor focus API not available`);\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error resetting selection`, error);\n }\n};\n/**\n * Detect document changes by collecting CommentNodes and analyzing them\n * Similar to Tiptap's detectDocumentChanges and old Lexical's onTransaction logic\n *\n * @param editor - Lexical editor instance\n * @param getAnnotationData - Function to get annotation data from state\n * @returns Map of annotation ID to AnnotationChange\n */\nexport const detectDocumentChanges = (editor, getAnnotationData) => {\n const logPrefix = '[detectDocumentChanges]';\n const changes = new Map();\n try {\n // Phase 1: Collect all CommentNodes from the document\n const annotationsToCheck = new Map();\n let commentNodeCount = 0;\n editor.getEditorState().read(() => {\n const root = $getRoot();\n let currentPosition = 0;\n const traverse = (node) => {\n if ($isCommentNode(node)) {\n commentNodeCount++;\n const annotationId = node.__annotationId;\n if (!annotationId) {\n Console.warn(`${logPrefix}[traverse] CommentNode found but no annotationId`);\n return;\n }\n // Get text content from CommentNode\n const text = node.getTextContent();\n const nodeSize = text.length;\n const nodeData = {\n node: { text, nodeSize },\n pos: currentPosition,\n };\n const existingAnnotation = annotationsToCheck.get(annotationId);\n if (existingAnnotation) {\n existingAnnotation.nodes.push(nodeData);\n }\n else {\n annotationsToCheck.set(annotationId, {\n id: annotationId,\n multiThreadAnnotationId: node.__multiThreadAnnotationId,\n nodes: [nodeData],\n });\n }\n currentPosition += nodeSize;\n }\n else if ($isTextNode(node)) {\n currentPosition += node.getTextContent().length;\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n traverse(child);\n }\n }\n };\n traverse(root);\n });\n // Phase 2: Process each annotation to detect changes\n for (const [annotationId, data] of Array.from(annotationsToCheck.entries())) {\n const annotationLogPrefix = `${logPrefix}[${annotationId}]`;\n const storedData = getAnnotationData(annotationId);\n if (!storedData) {\n Console.warn(`${annotationLogPrefix} No stored data found, skipping`);\n continue;\n }\n // Step 1: Detect content changes\n const sortedNodes = [...data.nodes].sort((a, b) => a.pos - b.pos);\n const currentText = sortedNodes.map((n) => n.node.text || '').join('');\n const contentChanged = currentText !== storedData.originalText;\n // Step 2: Detect container changes and get search results\n const containerResult = detectContainerChanges(editor, {\n annotationId: data.id,\n currentText,\n targetTextNodeId: storedData.targetTextNodeId,\n nodes: data.nodes,\n });\n const targetTextNodeIdChanged = containerResult.targetTextNodeIdChanged;\n const newTargetTextNodeId = containerResult.newTargetTextNodeId;\n const searchResults = containerResult.searchResults;\n // Step 3: Calculate occurrence changes\n const currentOccurrence = calculateNewOccurrence(editor, {\n searchResults,\n nodes: data.nodes,\n contentChanged,\n currentText,\n targetTextNodeId: targetTextNodeIdChanged ? newTargetTextNodeId : storedData.targetTextNodeId,\n originalOccurrence: storedData.originalOccurrence,\n });\n const occurrenceChanged = currentOccurrence !== storedData.originalOccurrence;\n // Step 4: Create change object if any changes detected\n if (contentChanged || occurrenceChanged || targetTextNodeIdChanged) {\n const change = {\n annotationId: data.id,\n multiThreadAnnotationId: data.multiThreadAnnotationId,\n originalText: storedData.originalText,\n currentText: contentChanged ? currentText : storedData.originalText,\n originalOccurrence: storedData.originalOccurrence,\n currentOccurrence,\n originalTargetTextNodeId: storedData.targetTextNodeId,\n newTargetTextNodeId: targetTextNodeIdChanged ? newTargetTextNodeId : storedData.targetTextNodeId,\n annotation: storedData.annotation,\n contentChanged,\n occurrenceChanged,\n targetTextNodeIdChanged,\n };\n changes.set(annotationId, change);\n }\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error detecting document changes`, error);\n }\n return changes;\n};\n/**\n * Detect container changes and get search results\n */\nconst detectContainerChanges = (editor, params) => {\n const { annotationId, currentText, targetTextNodeId } = params;\n let targetTextNodeIdChanged = false;\n let newTargetTextNodeId = targetTextNodeId;\n let searchResults = [];\n // If we have a targetTextNodeId, search within that container\n if (targetTextNodeId) {\n const containerElement = document.getElementById(targetTextNodeId);\n if (containerElement) {\n // Search within container\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText, nodeMap } = collectTextContent(root);\n const containerContent = getContainerTextContent(containerElement, editor, nodeMap, combinedText);\n if (containerContent) {\n const positions = kmpSearch(containerContent.text, currentText);\n for (const pos of positions) {\n searchResults.push({\n start: containerContent.startOffset + pos,\n end: containerContent.startOffset + pos + currentText.length,\n });\n }\n }\n });\n // If no results in container, try to find new container\n if (searchResults.length === 0) {\n const newContainer = findNewContainer(editor, currentText);\n if (newContainer && newContainer.id !== targetTextNodeId) {\n targetTextNodeIdChanged = true;\n newTargetTextNodeId = newContainer.id;\n // Search in new container\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText, nodeMap } = collectTextContent(root);\n const containerContent = getContainerTextContent(newContainer, editor, nodeMap, combinedText);\n if (containerContent) {\n const positions = kmpSearch(containerContent.text, currentText);\n for (const pos of positions) {\n searchResults.push({\n start: containerContent.startOffset + pos,\n end: containerContent.startOffset + pos + currentText.length,\n });\n }\n }\n });\n }\n }\n }\n }\n // If no container or no results, search entire document\n if (searchResults.length === 0) {\n editor.getEditorState().read(() => {\n const root = $getRoot();\n const { combinedText } = collectTextContent(root);\n const positions = kmpSearch(combinedText, currentText);\n for (const pos of positions) {\n searchResults.push({\n start: pos,\n end: pos + currentText.length,\n });\n }\n });\n }\n return {\n targetTextNodeIdChanged,\n newTargetTextNodeId,\n searchResults,\n };\n};\n/**\n * Find new container for moved content\n */\nconst findNewContainer = (editor, currentText) => {\n const editorElement = editor.getRootElement();\n if (!editorElement)\n return null;\n // Find all elements with IDs that contain the text\n const elementsWithIds = editorElement.querySelectorAll('[id]');\n for (const element of Array.from(elementsWithIds)) {\n if (element.textContent?.includes(currentText)) {\n return element;\n }\n }\n return null;\n};\n/**\n * Calculate new occurrence based on annotation position and search results\n */\nconst calculateNewOccurrence = (editor, params) => {\n if (params.searchResults.length === 0) {\n return params.originalOccurrence;\n }\n const firstNode = params.nodes[0];\n const lastNode = params.nodes[params.nodes.length - 1];\n const annotationRange = {\n from: firstNode.pos,\n to: lastNode.pos + (lastNode.node.nodeSize || 0),\n };\n // Find which occurrence matches the annotation range\n for (let i = 0; i < params.searchResults.length; i++) {\n const result = params.searchResults[i];\n if ((result.start <= annotationRange.from && result.end >= annotationRange.from) ||\n (result.start <= annotationRange.to && result.end >= annotationRange.to) ||\n (annotationRange.from <= result.start && annotationRange.to >= result.end)) {\n return i + 1; // 1-based index\n }\n }\n return params.originalOccurrence;\n};\n","/**\n * Velt SDK adapter.\n *\n * Purpose: High-level Velt SDK wrapper with comprehensive error handling.\n * This is the ONLY place where @veltdev/types and window.Velt may be accessed.\n *\n * Key responsibilities:\n * - Wrap all Velt SDK calls with error handling\n * - Provide clean interface for features layer\n * - Handle SDK unavailability gracefully\n * - Manage subscriptions to Velt events\n *\n * Dependencies:\n * - @veltdev/types (ONLY place allowed to import Velt types)\n * - types/common.ts (for CommentAnnotationContext)\n * - types/velt.ts (for re-exported Velt types)\n */\nimport { Console } from '@/utils/console';\n/**\n * Global state for managing selected annotations subscription\n */\nlet isSubscribed = false;\nlet selectedCommentsMap = new Map();\nconst subscribers = new Map();\n/**\n * Gets the Velt comment element from the SDK.\n * Returns null if SDK is unavailable or comment element cannot be accessed.\n *\n * @returns CommentElement or null if unavailable\n */\nexport const getCommentElement = () => {\n try {\n // Access window.Velt safely\n const velt = window.Velt;\n if (velt?.getCommentElement) {\n return velt.getCommentElement();\n }\n return null;\n }\n catch (error) {\n Console.catch('Error getting Velt comment element', error);\n return null;\n }\n};\n/**\n * Adds a comment via the Velt SDK.\n *\n * @param params Object containing context and optional location\n * @returns Promise resolving to CommentAnnotation or null if failed\n */\nexport const addVeltComment = (params) => {\n const logPrefix = '[addVeltComment]';\n try {\n const commentElement = getCommentElement();\n if (!commentElement) {\n Console.warn(`${logPrefix} Velt SDK not available, cannot add comment`);\n return Promise.resolve({ annotation: null, result: null });\n }\n return commentElement\n .addManualComment({ context: params.context, location: params.location })\n .then((result) => {\n if (result?.annotation) {\n return { annotation: result.annotation, result };\n }\n Console.warn(`${logPrefix} No annotation in result`);\n return { annotation: null, result };\n })\n .catch((error) => {\n Console.catch(`${logPrefix} Error adding Velt comment`, error);\n return { annotation: null, result: null };\n });\n }\n catch (error) {\n Console.catch(`${logPrefix} Exception adding Velt comment`, error);\n return Promise.resolve({ annotation: null, result: null });\n }\n};\n/**\n * Updates the context of an existing annotation in the Velt SDK.\n *\n * @param annotationId The annotation ID to update\n * @param context The new context to set\n */\nexport const updateAnnotationContext = (annotationId, context) => {\n try {\n const commentElement = getCommentElement();\n if (!commentElement) {\n Console.warn('Velt SDK not available, cannot update annotation context');\n return;\n }\n commentElement.updateContext(annotationId, context);\n }\n catch (error) {\n Console.catch('Error updating annotation context', error);\n }\n};\n/**\n * Subscribes to changes in selected annotations from the Velt SDK.\n * Returns an unsubscribe function.\n *\n * @param callback Function called when selected annotations change\n * @param subscriberId Optional unique ID for this subscriber\n * @returns Unsubscribe function\n */\nexport const subscribeToSelectedAnnotations = (callback, subscriberId) => {\n try {\n const id = subscriberId || `subscriber-${Date.now()}-${Math.random()}`;\n // Set up singleton subscription if not already subscribed\n if (!isSubscribed) {\n const commentElement = getCommentElement();\n if (commentElement && typeof commentElement.getSelectedComments === 'function') {\n commentElement.getSelectedComments().subscribe((comments) => {\n const newSelectedMap = new Map();\n comments?.forEach((comment) => {\n if (comment?.annotationId) {\n newSelectedMap.set(comment.annotationId, true);\n }\n });\n // Check if the maps are different\n const areMapsEqual = (map1, map2) => {\n if (map1.size !== map2.size)\n return false;\n for (const [key, value] of Array.from(map1)) {\n if (map2.get(key) !== value)\n return false;\n }\n return true;\n };\n // Skip update if maps are equal\n if (!areMapsEqual(newSelectedMap, selectedCommentsMap)) {\n selectedCommentsMap = newSelectedMap;\n // Convert to Set<string> for callback\n const selectedIds = new Set();\n for (const [annotationId] of Array.from(selectedCommentsMap)) {\n selectedIds.add(annotationId);\n }\n // Notify all subscribers\n subscribers.forEach((subscriber) => {\n if (typeof subscriber === 'function') {\n try {\n subscriber(selectedIds);\n }\n catch (error) {\n Console.catch('Error in selected annotations subscriber', error);\n }\n }\n });\n }\n });\n isSubscribed = true;\n }\n }\n // Register this subscriber\n subscribers.set(id, callback);\n // Return unsubscribe function\n return () => {\n subscribers.delete(id);\n };\n }\n catch (error) {\n Console.catch('Error subscribing to selected annotations', error);\n // Return no-op unsubscribe function\n return () => { };\n }\n};\n","/**\n * Core state management module.\n *\n * Purpose: Single source of truth for annotation state per editor.\n * This module manages annotation data and selected annotations for each editor instance.\n *\n * Key responsibilities:\n * - Create and manage per-editor annotation state\n * - CRUD operations for annotations\n * - Track selected annotations\n * - Ensure state isolation between editor instances\n *\n * CRITICAL: This module must NOT import Lexical or Velt types.\n * It only depends on types/*, constants/*, and utils/*.\n */\nimport { Console } from '@/utils/console';\n/**\n * Map of editor IDs to their annotation state\n */\nconst stateMap = new Map();\n/**\n * Create a new annotation state for an editor\n *\n * @param editorId - Unique identifier for the editor instance\n * @returns New AnnotationState instance\n */\nexport const createAnnotationState = (editorId) => {\n const state = {\n annotations: new Map(),\n commentAnnotations: [],\n selectedAnnotations: new Set(),\n editorId,\n };\n stateMap.set(editorId, state);\n return state;\n};\n/**\n * Get or create annotation state for an editor\n */\nconst getOrCreateState = (editorId) => {\n let state = stateMap.get(editorId);\n if (!state) {\n state = createAnnotationState(editorId);\n }\n return state;\n};\n/**\n * Updates annotations in state from Velt SDK data.\n * Converts CommentAnnotation[] to AnnotationData[] and stores them.\n *\n * This matches Tiptap's pattern where conversion happens in updateAnnotations,\n * not in the feature module (renderComments).\n *\n * @param editorId Unique identifier for the editor\n * @param annotations Array of CommentAnnotation from Velt SDK\n */\nexport const updateAnnotations = (editorId, annotations) => {\n const logPrefix = '[updateAnnotations]';\n try {\n const state = getOrCreateState(editorId);\n // Store full CommentAnnotation[] with status field for rendering\n state.commentAnnotations = annotations;\n // Clear existing annotations\n state.annotations.clear();\n // Convert CommentAnnotation[] to AnnotationData[] (nested structure)\n for (const annotation of annotations) {\n if (!annotation?.annotationId) {\n continue;\n }\n const annotationData = {\n annotationId: annotation.annotationId,\n multiThreadAnnotationId: annotation.multiThreadAnnotationId,\n context: annotation.context,\n // Position will be set when marks are applied\n };\n state.annotations.set(annotation.annotationId, annotationData);\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error updating annotations`, error);\n }\n};\n/**\n * Gets all annotations for an editor.\n *\n * @param editorId Unique identifier for the editor\n * @returns Map of annotation ID to AnnotationData\n */\nexport const getAllAnnotations = (editorId) => {\n try {\n const state = stateMap.get(editorId);\n if (!state) {\n return new Map();\n }\n return new Map(state.annotations);\n }\n catch (error) {\n Console.catch('Error getting all annotations', error);\n return new Map();\n }\n};\n/**\n * Get a specific annotation by ID\n *\n * @param editorId - Editor identifier\n * @param annotationId - Annotation ID\n * @returns Annotation or null if not found\n */\nexport const getAnnotation = (editorId, annotationId) => {\n const logPrefix = '[getAnnotation]';\n try {\n const state = stateMap.get(editorId);\n if (!state) {\n return null;\n }\n const annotation = state.annotations.get(annotationId) || null;\n return annotation;\n }\n catch (error) {\n Console.catch(`${logPrefix} Error getting annotation`, error);\n return null;\n }\n};\n/**\n * Remove a specific annotation\n *\n * @param editorId - Editor identifier\n * @param annotationId - Annotation ID to remove\n */\nexport const removeAnnotation = (editorId, annotationId) => {\n try {\n const state = stateMap.get(editorId);\n if (!state) {\n return;\n }\n state.annotations.delete(annotationId);\n state.selectedAnnotations.delete(annotationId);\n }\n catch (error) {\n Console.catch('Error removing annotation', error);\n }\n};\n/**\n * Clears all state for an editor (used during cleanup).\n * Matches Tiptap's clearState function signature.\n *\n * @param editorId Unique identifier for the editor\n */\nexport const clearState = (editorId) => {\n stateMap.delete(editorId);\n};\n/**\n * Gets the set of selected annotation IDs for an editor.\n *\n * @param editorId - Unique identifier for the editor\n * @returns Set of selected annotation IDs\n */\nexport const getSelectedAnnotations = (editorId) => {\n try {\n const state = stateMap.get(editorId);\n if (!state) {\n return new Set();\n }\n return new Set(state.selectedAnnotations);\n }\n catch (error) {\n Console.catch('Error getting selected annotations', error);\n return new Set();\n }\n};\n/**\n * Updates the set of selected annotation IDs for an editor.\n *\n * @param editorId - Unique identifier for the editor\n * @param selectedIds - Set of selected annotation IDs\n */\nexport const setSelectedAnnotations = (editorId, selectedIds) => {\n try {\n const state = getOrCreateState(editorId);\n state.selectedAnnotations = new Set(selectedIds);\n }\n catch (error) {\n Console.catch('Error setting selected annotations', error);\n }\n};\n/**\n * Get all comment annotations (with status) for an editor.\n * Returns the full CommentAnnotation[] array stored in state.\n *\n * @param editorId Unique identifier for the editor\n * @returns Array of CommentAnnotation with status field, or empty array if none\n */\nexport const getCommentAnnotations = (editorId) => {\n try {\n const state = stateMap.get(editorId);\n return state?.commentAnnotations || [];\n }\n catch (error) {\n Console.catch('Error getting comment annotations', error);\n return [];\n }\n};\n","/**\n * Core registry module.\n *\n * Purpose: Maintains a registry mapping editorId → editor context.\n * Replaces the singleton pattern from legacy Plugin class with a proper registry.\n *\n * Key responsibilities:\n * - Register editor instances with their configuration\n * - Retrieve editor contexts by ID\n * - Unregister editors when they're destroyed\n * - Extract editor IDs from editor instances\n *\n * CRITICAL: This module must NOT import Lexical or Velt types directly.\n * It uses adapter interfaces from types/* instead.\n *\n * CRITICAL: This module is PURE registry logic - no document change listeners,\n * no feature imports, no adapter imports. Document change handling belongs in extension.ts.\n *\n * Dependencies: types/common.ts, core/state.ts, adapters/host/doc.ts\n */\nimport { getEditorId } from '@/adapters/host/doc';\nimport { Constants } from '@/constants/common';\nimport { clearState, createAnnotationState } from '@/core/state';\nimport { Console } from '@/utils/console';\n/**\n * Registry mapping editorId -> EditorContext\n */\nconst registry = new Map();\n/**\n * Registers an editor instance in the registry.\n * Creates initial state for the editor.\n *\n * @param editorId Unique identifier for the editor\n * @param editor The editor instance\n * @param config Extension configuration options\n * @returns EditorContext for the registered editor\n */\nexport const registerEditor = (editorId, editor, config) => {\n // Ensure editorId is set\n const finalEditorId = editorId || Constants.DEFAULT_EDITOR_ID;\n // Create initial state for this editor\n createAnnotationState(finalEditorId);\n const context = {\n editorId: finalEditorId,\n editor,\n config: config || {},\n };\n registry.set(finalEditorId, context);\n return context;\n};\n/**\n * Retrieves the editor context for a given editor ID.\n *\n * @param editorId Unique identifier for the editor\n * @returns EditorContext or null if not found\n */\nexport const getEditorContext = (editorId) => {\n const context = registry.get(editorId);\n return context || null;\n};\n/**\n * Unregisters an editor from the registry and cleans up its state.\n *\n * @param editorId Unique identifier for the editor\n */\nexport const unregisterEditor = (editorId) => {\n registry.delete(editorId);\n clearState(editorId);\n};\n/**\n * Ensures an editor is registered (lazy registration helper).\n * Gets existing registration if available, creates new if not found.\n *\n * NOTE: This does NOT set up document change listeners - that belongs in extension.ts.\n * This is pure registry logic only.\n *\n * @param editorId Editor identifier (optional, will be resolved from editor if not provided)\n * @param editor Editor instance\n * @param config Optional configuration\n * @returns EditorContext\n */\nexport const ensureEditorRegistered = (editorId, editor, config) => {\n // Resolve editorId if not provided\n let finalEditorId = editorId;\n if (!finalEditorId) {\n finalEditorId = getEditorId(editor) || Constants.DEFAULT_EDITOR_ID;\n }\n // Check if already registered\n const existing = getEditorContext(finalEditorId);\n if (existing) {\n return existing;\n }\n // Register new editor\n return registerEditor(finalEditorId, editor, config);\n};\n/**\n * Extracts the editor ID from an editor instance.\n * Checks editor storage first, then DOM attributes.\n *\n * @param editor The editor instance\n * @returns Editor ID or null if not found\n */\nexport const getEditorIdFromEditor = (editor) => {\n try {\n // Use the host adapter to extract editor ID\n // This avoids importing Lexical/Tiptap types here\n return getEditorId(editor);\n }\n catch (error) {\n Console.catch('Error extracting editor ID', error);\n return null;\n }\n};\n/**\n * Gets all registered editor IDs.\n *\n * @returns Array of editor IDs\n */\nexport const getAllEditorIds = () => {\n return Array.from(registry.keys());\n};\n","/**\n * Update content feature module.\n *\n * Purpose: Handle document changes and annotation context updates.\n * Orchestrates adapters and core to detect and process annotation changes.\n *\n * Key responsibilities:\n * - Detect document changes during transactions\n * - Compare current state with original annotations\n * - Detect content, occurrence, and container changes\n * - Update Velt contexts for changed annotations\n * - Update marks in editor if needed\n *\n * Dependencies:\n * - adapters/host/doc.ts (for change detection)\n * - adapters/velt.ts (for context updates)\n * - adapters/host/marks.ts (for mark updates)\n * - core/state.ts (for state access)\n * - types/host.ts (for AnnotationChange)\n *\n * IMPORTANT: This module MUST NOT import Lexical or Velt types directly.\n * All SDK access goes through adapters.\n */\nimport { detectDocumentChanges } from '@/adapters/host/doc';\nimport { updateMarks } from '@/adapters/host/marks';\nimport { updateAnnotationContext } from '@/adapters/velt';\nimport { getAnnotation } from '@/core/state';\nimport { Console } from '@/utils/console';\n/**\n * Handles content updates when document changes occur.\n * Detects annotation changes and updates Velt contexts accordingly.\n *\n * @param editorId Unique identifier for the editor\n * @param editor The editor instance\n * @param transaction The transaction object\n */\nexport const handleContentUpdate = (editorId, editor, transaction) => {\n const logPrefix = '[handleContentUpdate]';\n try {\n // Step 1: Detect document changes using host adapter\n // This requires a function to get annotation data from state\n const getAnnotationData = (annotationId) => {\n const annotation = getAnnotation(editorId, annotationId);\n if (!annotation) {\n Console.warn(`${logPrefix} Annotation not found in state: ${annotationId}`);\n return null;\n }\n const textEditorConfig = annotation.context?.textEditorConfig;\n const originalText = textEditorConfig?.text || '';\n const originalOccurrence = textEditorConfig?.occurrence || 1;\n const targetTextNodeId = textEditorConfig?.targetTextNodeId || '';\n return {\n annotation: annotation,\n originalText,\n originalOccurrence,\n targetTextNodeId,\n };\n };\n const changes = detectDocumentChanges(editor, getAnnotationData);\n if (changes.size === 0) {\n return;\n }\n // Step 1.5: Update marks if needed\n updateMarks(editor, changes);\n // Step 2: Process each change (matching legacy updateVeltComments logic)\n for (const [annotationId, change] of Array.from(changes.entries())) {\n const changeLogPrefix = `${logPrefix}[${annotationId}]`;\n const annotation = getAnnotation(editorId, annotationId);\n if (!annotation) {\n Console.warn(`${changeLogPrefix} Annotation not found in state, skipping`);\n continue;\n }\n // Step 3: Build updated context (matching Tiptap's deep clone pattern)\n if (!annotation.context?.textEditorConfig) {\n Console.warn(`${changeLogPrefix} No textEditorConfig, skipping`);\n continue;\n }\n const textEditorConfig = JSON.parse(JSON.stringify(annotation.context.textEditorConfig));\n if (change.contentChanged) {\n textEditorConfig.text = change.currentText;\n }\n if (change.occurrenceChanged) {\n textEditorConfig.occurrence = change.currentOccurrence;\n }\n if (change.targetTextNodeIdChanged) {\n textEditorConfig.targetTextNodeId = change.newTargetTextNodeId;\n }\n const updatedContext = {\n ...annotation.context,\n textEditorConfig,\n };\n // Step 4: Update Velt context\n updateAnnotationContext(annotationId, updatedContext);\n // Note: Local state is updated when renderComments is called with fresh annotations from Velt.\n // This avoids state duplication and ensures state stays in sync with Velt SDK as source of truth.\n }\n }\n catch (error) {\n Console.catch('Error handling content update', error);\n }\n};\n","/**\n * Core extension module.\n *\n * Purpose: Manages document change listeners and editor setup for Lexical editors.\n * This module handles the lifecycle of document change subscriptions.\n *\n * Key responsibilities:\n * - Set up document change listeners for editors\n * - Wire up document change handler to updateContent feature\n * - Manage document change listener lifecycle\n * - Orchestrate editor setup (registration + listeners)\n *\n * Dependencies: adapters/host/doc.ts, features/updateContent.ts, core/registry.ts\n */\nimport { subscribeToDocumentChanges } from '@/adapters/host/doc';\nimport { Constants } from '@/constants/common';\nimport { ensureEditorRegistered } from '@/core/registry';\nimport { handleContentUpdate } from '@/features/updateContent';\nimport { Console } from '@/utils/console';\n/**\n * Map tracking document change listener subscriptions: editorId → unsubscribe function\n * This ensures listeners are set up for lazy registration scenarios.\n */\nconst documentChangeSubscriptions = new Map();\n/**\n * Ensures document change listeners are set up for an editor.\n * This is called by lazy registration (addComment/renderComments) to ensure content updates work.\n *\n * @param editorId Editor identifier\n * @param editor Lexical editor instance\n * @returns true if listeners were set up (or already existed), false if setup failed\n */\nexport const ensureDocumentChangeListeners = (editorId, editor) => {\n // Check if listeners already set up\n if (documentChangeSubscriptions.has(editorId)) {\n return true;\n }\n try {\n // Set up document change subscription (matching Tiptap's onTransaction pattern)\n const unsubscribeDocChanges = subscribeToDocumentChanges(editor, () => {\n // Handle content updates (matching Tiptap's onTransaction -> handleContentUpdate pattern)\n // Note: Lexical doesn't have transactions, so we pass undefined\n handleContentUpdate(editorId, editor, undefined);\n });\n // Store unsubscribe function\n documentChangeSubscriptions.set(editorId, unsubscribeDocChanges);\n return true;\n }\n catch (error) {\n Console.catch('[ensureDocumentChangeListeners] Error setting up document change listeners', error);\n return false;\n }\n};\n/**\n * Ensures editor is set up (registered and document listeners configured).\n * This is a unified orchestration function that handles both registration and setup.\n *\n * @param editorId Optional editor ID (will be resolved from editor if not provided)\n * @param editor The editor instance (typed as unknown to avoid importing editor types in feature modules)\n * @returns Object with editorId string\n *\n * @remarks\n * - This function abstracts editor-specific setup logic\n * - For Lexical, ensures editor is registered and document change listeners are set up\n * - Fails silently if setup fails\n */\nexport const ensureEditorSetup = (editorId, editor) => {\n const logPrefix = '[ensureEditorSetup]';\n try {\n // Ensure editor is registered (lazy registration)\n const editorContext = ensureEditorRegistered(editorId, editor);\n const finalEditorId = editorContext.editorId;\n // Ensure document change listeners are set up\n ensureDocumentChangeListeners(finalEditorId, editor);\n return { editorId: finalEditorId };\n }\n catch (error) {\n Console.catch(`${logPrefix} Error setting up editor`, error);\n return { editorId: editorId || Constants.DEFAULT_EDITOR_ID };\n }\n};\n","/**\n * Comment renderer module.\n *\n * Purpose: Handle filtering, comparison, and mark updates for comment annotations.\n * This module manages the rendering state and applies marks based on resolved/selected status.\n *\n * Key responsibilities:\n * - Filter annotations based on status (terminal/resolved) and selected state\n * - Compare previous vs current filtered annotations to detect removals\n * - Remove marks for annotations that should no longer be shown\n * - Re-apply marks for all filtered annotations\n *\n * Dependencies:\n * - core/state.ts (for reading annotations and selected state)\n * - adapters/host/marks.ts (for applying/removing marks)\n * - types/velt.ts (for CommentAnnotation)\n * - types/state.ts (for AnnotationData)\n *\n * IMPORTANT: This module MUST NOT import editor or Velt types directly.\n * All SDK access goes through adapters.\n */\nimport { applyAnnotationMarks, removeAnnotationMarks } from '@/adapters/host/marks';\nimport { Constants } from '@/constants/common';\nimport { getAllAnnotations, getCommentAnnotations, getSelectedAnnotations } from '@/core/state';\nimport { Console } from '@/utils/console';\nconst rendererStore = new Map();\n/**\n * Filters annotations based on status and selected state.\n * Excludes terminal/resolved comments unless they're selected.\n *\n * @param annotations Array of CommentAnnotation to filter\n * @param selectedIds Set of selected annotation IDs\n * @returns Filtered array of CommentAnnotation\n */\nconst filterAnnotations = (annotations, selectedIds) => {\n return annotations.filter((annotation) => {\n // Include if not terminal/resolved\n if (annotation?.status?.type !== Constants.STATUS_TERMINAL) {\n return true;\n }\n // Include terminal/resolved only if selected\n return selectedIds.has(annotation.annotationId);\n });\n};\n/**\n * Converts filtered CommentAnnotation[] to AnnotationData[] for Lexical adapter.\n * Uses existing AnnotationData from state when available.\n *\n * @param editorId Editor identifier\n * @param filteredAnnotations Array of filtered CommentAnnotation\n * @returns Array of AnnotationData for Lexical adapter\n */\nconst convertToAnnotationData = (editorId, filteredAnnotations) => {\n // Get existing AnnotationData from state\n const existingAnnotations = getAllAnnotations(editorId);\n const existingMap = new Map();\n for (const [id, ann] of existingAnnotations) {\n existingMap.set(id, ann);\n }\n // Convert filtered CommentAnnotation[] to AnnotationData[]\n const annotationDataArray = [];\n for (const annotation of filteredAnnotations) {\n if (!annotation.annotationId) {\n continue;\n }\n // Use existing AnnotationData if available (preserves position)\n const existing = existingMap.get(annotation.annotationId);\n if (existing) {\n annotationDataArray.push(existing);\n }\n else {\n // Create new AnnotationData from CommentAnnotation\n annotationDataArray.push({\n annotationId: annotation.annotationId,\n multiThreadAnnotationId: annotation.multiThreadAnnotationId,\n context: annotation.context,\n // Position will be calculated during rendering\n });\n }\n }\n return annotationDataArray;\n};\n/**\n * Updates comments for an editor by filtering, comparing, removing, and re-applying marks.\n * This is the core rendering logic that handles resolved comment filtering.\n *\n * @param editorId Unique identifier for the editor\n * @param editor The editor instance\n * @param annotations All CommentAnnotation from Velt SDK (with status)\n */\nexport const updateComments = (editorId, editor, annotations) => {\n const logPrefix = '[commentRenderer:updateComments]';\n try {\n // Store editor instance in renderer store\n const storeEntry = rendererStore.get(editorId);\n if (storeEntry) {\n storeEntry.editor = editor;\n }\n else {\n rendererStore.set(editorId, {\n editor,\n previousFilteredAnnotations: [],\n });\n }\n // Get selected annotations from state\n const selectedIds = getSelectedAnnotations(editorId);\n // Filter annotations based on status and selected state\n const filteredAnnotations = filterAnnotations(annotations, selectedIds);\n // Get previous filtered annotations for comparison\n const previousFilteredAnnotations = rendererStore.get(editorId)?.previousFilteredAnnotations || [];\n // Create sets of annotation IDs for comparison\n const newAnnotationIds = new Set(filteredAnnotations.map((ann) => ann.annotationId).filter(Boolean));\n const previousAnnotationIds = new Set(previousFilteredAnnotations.map((ann) => ann.annotationId).filter(Boolean));\n // Find annotations that should be removed (were in previous, not in new)\n const removedAnnotationIds = Array.from(previousAnnotationIds).filter((id) => !newAnnotationIds.has(id));\n // Remove marks for removed annotations (Lexical uses array-based API)\n if (removedAnnotationIds.length > 0) {\n removeAnnotationMarks(editor, removedAnnotationIds);\n }\n // Update previous filtered annotations in renderer store\n const currentStoreEntry = rendererStore.get(editorId);\n if (currentStoreEntry) {\n currentStoreEntry.previousFilteredAnnotations = filteredAnnotations;\n }\n // Convert filtered CommentAnnotation[] to AnnotationData[] and apply marks\n // This ensures newly selected resolved comments are highlighted\n const annotationDataArray = convertToAnnotationData(editorId, filteredAnnotations);\n if (annotationDataArray.length > 0) {\n applyAnnotationMarks(editor, annotationDataArray);\n }\n }\n catch (error) {\n Console.catch(`${logPrefix} Error updating comments`, error);\n }\n};\n/**\n * Updates comments when selected annotations change.\n * Re-reads annotations from state and triggers re-rendering.\n *\n * @param editorId Unique identifier for the editor\n * @param selectedIds Set of selected annotation IDs (already updated in state)\n */\nexport const updateSelection = (editorId, selectedIds) => {\n const logPrefix = '[commentRenderer:updateSelection]';\n try {\n // Get editor instance from renderer store\n const storeEntry = rendererStore.get(editorId);\n if (!storeEntry || !storeEntry.editor) {\n Console.warn(`${logPrefix} No editor found for editorId: ${editorId}`);\n return;\n }\n // Get all comment annotations from state (with status)\n const annotations = getCommentAnnotations(editorId);\n // Re-run updateComments with current annotations and new selection\n // This will filter, compare, remove, and re-apply marks\n updateComments(editorId, storeEntry.editor, annotations);\n }\n catch (error) {\n Console.catch(`${logPrefix} Error updating selection`, error);\n }\n};\n/**\n * Cleans up renderer store for an editor.\n * Should be called when editor is destroyed.\n *\n * @param editorId Unique identifier for the editor\n */\nexport const cleanupRenderer = (editorId) => {\n rendererStore.delete(editorId);\n};\n","/**\n * Render comments feature module.\n *\n * Purpose: Public API entry point for rendering comment annotations.\n * Orchestrates state updates, subscriptions, and delegates to commentRenderer.\n *\n * Key responsibilities:\n * - Update state with new annotations\n * - Subscribe to selected annotations from Velt\n * - Delegate rendering logic to commentRenderer\n *\n * Dependencies:\n * - adapters/velt.ts (for Velt SDK subscription)\n * - adapters/host/doc.ts (for editor setup)\n * - core/state.ts (for state management)\n * - core/registry.ts (for editor ID extraction)\n * - features/commentRenderer.ts (for rendering logic)\n * - types/velt.ts (for CommentAnnotation)\n *\n * IMPORTANT: This module MUST NOT import editor or Velt types directly.\n * All SDK access goes through adapters.\n */\nimport { subscribeToSelectedAnnotations } from '@/adapters/velt';\nimport { Constants } from '@/constants/common';\nimport { ensureEditorSetup } from '@/core/extension';\nimport { setSelectedAnnotations, updateAnnotations } from '@/core/state';\nimport { cleanupRenderer, updateComments, updateSelection } from '@/features/commentRenderer';\nimport { Console } from '@/utils/console';\n/**\n * Subscription map to track subscriptions per editor\n */\nconst subscriptionMap = new Map();\n/**\n * Renders comment annotations as marks in the editor.\n *\n * This function renders comment annotations as visual marks in the editor.\n * It filters annotations for the specific editor and applies marks accordingly.\n *\n * @param request - Object containing editor, editorId (optional), and commentAnnotations (optional)\n *\n * @example\n * ```typescript\n * // Simple usage\n * renderComments({ editor });\n *\n * // With editor ID\n * renderComments({ editorId: 'my-editor', editor });\n *\n * // With annotations\n * renderComments({\n * editorId: 'my-editor',\n * editor,\n * commentAnnotations: [\n * {\n * annotationId: 'ann-123',\n * context: {\n * textEditorConfig: {\n * text: 'Hello world',\n * occurrence: 1,\n * editorId: 'my-editor'\n * }\n * }\n * }\n * ]\n * });\n * ```\n *\n * @remarks\n * - Only annotations with matching editorId are rendered\n * - Automatically subscribes to selected annotations changes\n * - Filters out terminal/resolved comments unless they're selected\n * - Removes marks when comments become resolved\n * - Re-applies marks when resolved comments are selected\n */\nexport const renderComments = ({ editor, editorId, commentAnnotations, }) => {\n const logPrefix = '[renderComments]';\n try {\n if (!editor) {\n Console.warn(`${logPrefix} No editor provided`);\n return;\n }\n // Validate commentAnnotations silently - skip if not available or not an array\n if (!commentAnnotations || !Array.isArray(commentAnnotations)) {\n commentAnnotations = [];\n }\n // Step 0: Ensure editor is set up (registered and document listeners configured)\n const { editorId: finalEditorId } = ensureEditorSetup(editorId, editor);\n // Deep clone annotations to avoid mutations\n const clonedAnnotations = JSON.parse(JSON.stringify(commentAnnotations));\n // Filter annotations for this specific editor\n // - If finalEditorId is DEFAULT_EDITOR_ID, match annotations without editorId OR with editorId === DEFAULT_EDITOR_ID\n // - Otherwise, match annotations with exact editorId match\n const editorAnnotations = clonedAnnotations.filter((annotation) => {\n if (!annotation?.context?.textEditorConfig) {\n return false;\n }\n const annotationEditorId = annotation.context.textEditorConfig.editorId;\n // Default editor matches: no editorId OR explicit DEFAULT_EDITOR_ID\n if (finalEditorId === Constants.DEFAULT_EDITOR_ID) {\n return annotationEditorId === undefined || annotationEditorId === Constants.DEFAULT_EDITOR_ID;\n }\n // For user-provided editorIds, require exact match\n return annotationEditorId === finalEditorId;\n });\n // Step 1: Update state with filtered annotations (stores both AnnotationData and CommentAnnotation[])\n updateAnnotations(finalEditorId, editorAnnotations);\n // Step 2: Set up subscription to selected annotations (singleton pattern)\n if (!subscriptionMap.has(finalEditorId)) {\n // Capture finalEditorId in const to ensure type safety in closure\n const editorIdForSubscription = finalEditorId;\n const unsubscribe = subscribeToSelectedAnnotations((selectedIds) => {\n // Update selected annotations in state\n setSelectedAnnotations(editorIdForSubscription, selectedIds);\n // Trigger re-rendering with new selection\n updateSelection(editorIdForSubscription, selectedIds);\n }, editorIdForSubscription);\n subscriptionMap.set(finalEditorId, unsubscribe);\n }\n // Step 3: Delegate to commentRenderer to handle filtering, comparison, removal, and re-application\n updateComments(finalEditorId, editor, editorAnnotations);\n }\n catch (error) {\n Console.catch(`${logPrefix} Error rendering comments`, error);\n }\n};\n/**\n * Cleans up subscriptions and renderer state for an editor.\n * Should be called when editor is destroyed.\n *\n * @param editorId Unique identifier for the editor\n */\nexport const cleanupRenderComments = (editorId) => {\n // Clean up subscription\n const unsubscribe = subscriptionMap.get(editorId);\n if (unsubscribe) {\n unsubscribe();\n subscriptionMap.delete(editorId);\n }\n // Clean up renderer store\n cleanupRenderer(editorId);\n};\n","/**\n * Serializer utilities for Lexical editor state\n *\n * This module provides utilities for serializing Lexical editor state,\n * particularly for removing comment nodes from serialized JSON.\n *\n * Migrated from src/serializer.ts\n */\nimport { Constants } from '@/constants/common';\n/** Use the node's canonical type string */\nconst COMMENT_TYPE = Constants.NODE_TYPE;\n/**\n * Export JSON without comment nodes.\n *\n * This function serializes the editor state and removes all comment nodes,\n * unwrapping their children and normalizing adjacent text nodes.\n *\n * @param editor - Lexical editor instance\n * @returns Serialized editor state without comment nodes\n */\nexport const exportJSONWithoutComments = (editor) => {\n const state = editor.getEditorState();\n return state.read(() => {\n // The runtime JSON (from Lexical) may include `selection`.\n const raw = state.toJSON();\n // 1) Strip comment nodes from the root\n const strippedRoot = stripCommentsFromRoot(raw.root);\n // 2) Normalize adjacent text nodes created by unwrapping\n const normalizedRoot = normalizeTextNodesInRoot(strippedRoot);\n // 3) Return only the shape Lexical expects for SerializedEditorState\n const result = {\n root: normalizedRoot,\n };\n return result;\n });\n};\n/* -------------------- internals (strictly typed) -------------------- */\nconst stripCommentsFromRoot = (root) => {\n const nextChildren = stripFromChildrenArray(root.children);\n return {\n ...root,\n children: nextChildren,\n };\n};\n/**\n * Flattens comment nodes inside a children array and recurses.\n */\nconst stripFromChildrenArray = (children) => {\n const out = [];\n for (const child of children) {\n if (child.type === COMMENT_TYPE) {\n // Unwrap: push processed grandchildren directly\n if (isElementNode(child)) {\n const flattened = stripFromChildrenArray(child.children);\n out.push(...flattened);\n }\n // If a non-element comment node ever appears, drop it.\n continue;\n }\n out.push(stripCommentsFromNode(child));\n }\n return out;\n};\nconst stripCommentsFromNode = (node) => {\n if (isElementNode(node)) {\n const nextChildren = stripFromChildrenArray(node.children);\n return {\n ...node,\n children: nextChildren,\n };\n }\n return node;\n};\n/* -------------------- normalization (strictly typed) -------------------- */\nconst normalizeTextNodesInRoot = (root) => {\n const normalizedKids = root.children.map(normalizeTextNodesInNode);\n return {\n ...root,\n children: mergeAdjacentTextNodes(normalizedKids),\n };\n};\nconst normalizeTextNodesInNode = (node) => {\n if (isElementNode(node)) {\n const normalizedKids = node.children.map(normalizeTextNodesInNode);\n return {\n ...node,\n children: mergeAdjacentTextNodes(normalizedKids),\n };\n }\n return node;\n};\nconst mergeAdjacentTextNodes = (children) => {\n if (children.length < 2)\n return children;\n const merged = [];\n let i = 0;\n while (i < children.length) {\n const cur = children[i];\n if (isTextNode(cur)) {\n let text = cur.text ?? '';\n let j = i + 1;\n while (j < children.length) {\n const nxt = children[j];\n if (isTextNode(nxt) && haveSameTextFormat(cur, nxt)) {\n text += nxt.text ?? '';\n j++;\n }\n else {\n break;\n }\n }\n merged.push({ ...cur, text });\n i = j;\n }\n else {\n merged.push(cur);\n i++;\n }\n }\n return merged;\n};\n/* -------------------- type guards & equality -------------------- */\nconst isElementNode = (node) => {\n // SerializedElementNode always has a 'children' array\n return Object.prototype.hasOwnProperty.call(node, 'children');\n};\nconst isTextNode = (node) => {\n return node.type === 'text';\n};\nconst haveSameTextFormat = (a, b) => {\n return (a.format === b.format &&\n a.style === b.style &&\n a.mode === b.mode &&\n a.detail === b.detail);\n};\n","/**\n * Add comment feature module.\n *\n * Purpose: End-to-end add comment orchestration.\n * Orchestrates adapters and core to implement the add comment flow.\n *\n * Key responsibilities:\n * - Get Velt comment element\n * - Extract selection context from editor\n * - Create comment via Velt SDK\n * - Apply mark to editor document\n * - Update state with annotation data\n *\n * Dependencies:\n * - adapters/velt.ts (for Velt SDK calls)\n * - adapters/host/doc.ts (for selection extraction, editor setup, and selection reset)\n * - adapters/host/marks.ts (for mark application)\n * - adapters/host/storage.ts (for mark application configuration)\n * - core/state.ts (for state updates)\n * - types/common.ts (for CommentAnnotationContext)\n *\n * IMPORTANT: This module MUST NOT import editor or Velt types directly.\n * All SDK access goes through adapters.\n */\nimport { getCurrentSelectionContext, resetEditorSelection } from '@/adapters/host/doc';\nimport { applyAnnotationMark } from '@/adapters/host/marks';\nimport { shouldApplyMark } from '@/adapters/host/storage';\nimport { addVeltComment, getCommentElement } from '@/adapters/velt';\nimport { Constants } from '@/constants/common';\nimport { ensureEditorSetup } from '@/core/extension';\nimport { updateAnnotations } from '@/core/state';\nimport { Console } from '@/utils/console';\n/**\n * Adds a comment to the currently selected text in the editor.\n *\n * This is the main entry point for adding comments. It handles:\n * - Extracting selection context from the editor\n * - Creating the comment via Velt SDK\n * - Applying marks to the editor if configured\n * - Updating state with annotation data\n *\n * @param request - Object containing editorId (optional), editor, and context (optional)\n * @returns Promise that resolves when the comment is added (or fails silently)\n *\n * @example\n * ```typescript\n * // Simple usage\n * await addComment({ editor });\n *\n * // With editor ID\n * await addComment({ editorId: 'my-editor', editor });\n *\n * // With custom context\n * await addComment({\n * editorId: 'my-editor',\n * editor,\n * context: {\n * userId: 'user123',\n * metadata: { source: 'web-app' }\n * }\n * });\n * ```\n *\n * @remarks\n * - Requires text to be selected in the editor\n * - Requires Velt SDK to be available (window.Velt)\n * - Marks are only applied if persistVeltMarks is FALSE in extension config (matching legacy behavior)\n * - Fails silently if Velt SDK is unavailable or selection is invalid\n */\nexport async function addComment({ editorId, editor, context: clientContext, }) {\n const logPrefix = '[addComment]';\n // Validate editor parameter\n if (!editor) {\n Console.catch(`${logPrefix} ERROR: No editor provided`);\n return;\n }\n try {\n // Step 1: Get Velt comment element\n const commentElement = getCommentElement();\n if (!commentElement) {\n Console.warn(`${logPrefix} Velt SDK not available, cannot add comment`);\n return;\n }\n // Step 2: Extract selection context from editor\n // Type assertion needed because editor type is generic (unknown) to avoid importing editor types\n const selectionContext = getCurrentSelectionContext(editor);\n if (!selectionContext) {\n Console.warn(`${logPrefix} No valid selection found, exiting`);\n return;\n }\n // Step 3: Ensure editor is set up (registered and document listeners configured)\n const { editorId: finalEditorId } = ensureEditorSetup(editorId, editor);\n // Step 4: Build context object for Velt SDK\n // Only include editorId in textEditorConfig if user explicitly provided it\n // (either directly or via editor storage/DOM). DEFAULT_EDITOR_ID means it was auto-generated,\n // so we omit it for backward compatibility with old data that has no editorId.\n const userProvidedEditorId = editorId !== undefined || finalEditorId !== Constants.DEFAULT_EDITOR_ID;\n const context = {\n ...clientContext,\n textEditorConfig: {\n text: selectionContext.text,\n occurrence: selectionContext.occurrence,\n ...(userProvidedEditorId && { editorId: finalEditorId }),\n targetTextNodeId: selectionContext.targetTextNodeId,\n },\n };\n // Step 5: Build location object if locationId is available\n let location;\n if (selectionContext.locationId) {\n location = { id: selectionContext.locationId };\n }\n // Step 6: Add comment via Velt SDK (matching legacy behavior - mark applied in callback)\n const veltResult = await addVeltComment({ context, location });\n const annotation = veltResult.annotation;\n if (!annotation) {\n Console.warn(`${logPrefix} Failed to create comment via Velt SDK`);\n return;\n }\n // Step 7: Check if marks should be applied (matching legacy behavior)\n // This means: if persistVeltMarks is FALSE, apply the mark\n const shouldApplyMarkValue = shouldApplyMark(editor, Constants.STORAGE_KEY);\n // Step 8: Apply mark to editor if needed (matching legacy behavior)\n // Legacy applies mark in the .then() callback, but we've already awaited, so apply now\n if (shouldApplyMarkValue && annotation.annotationId) {\n applyAnnotationMark(editor, annotation.annotationId, annotation.multiThreadAnnotationId, selectionContext.from, selectionContext.to, selectionContext.occurrence);\n }\n // Step 9: Update state with new annotation\n if (annotation.annotationId) {\n updateAnnotations(finalEditorId, [annotation]);\n }\n else {\n Console.warn(`${logPrefix} No annotationId, skipping state update`);\n }\n // Step 10: Reset editor selection (matching legacy behavior)\n resetEditorSelection(editor);\n }\n catch (error) {\n Console.catch(`${logPrefix} Error adding comment`, error);\n }\n}\n","/**\n * Storage adapter for editor-specific storage access.\n *\n * Purpose: Abstracts away editor-specific storage patterns.\n * This allows the codebase to be more generic and reusable across different editor implementations.\n *\n * Key responsibilities:\n * - Get persistVeltMarks setting (for Lexical, always returns true - marks are always applied)\n * - Determine if marks should be applied\n *\n * Dependencies:\n * - lexical (ONLY place allowed to import LexicalEditor types for storage access)\n */\nimport { Console } from '@/utils/console';\n/**\n * Gets persistVeltMarks setting from storage.\n * For Lexical, marks are always applied, so this always returns true.\n *\n * @param editor The editor instance\n * @param storageKey The storage key (unused for Lexical, kept for API compatibility)\n * @returns persistVeltMarks boolean value (always true for Lexical)\n */\nexport const getPersistVeltMarks = (editor, storageKey = 'lexicalVeltComments') => {\n // Lexical always applies marks (no config option)\n return true;\n};\n/**\n * Determines if marks should be applied based on persistVeltMarks setting.\n * This is a unified adapter function that abstracts the mark application logic.\n *\n * @param editor The editor instance (typed as unknown to avoid importing editor types in feature modules)\n * @param storageKey The storage key (default: 'lexicalVeltComments')\n * @returns boolean indicating if marks should be applied\n *\n * @remarks\n * - For TipTap: checks persistVeltMarks setting (apply if FALSE)\n * - For Lexical: always returns true (always apply marks)\n * - Defaults to true if storage is unavailable\n */\nexport const shouldApplyMark = (editor, storageKey = 'lexicalVeltComments') => {\n try {\n // For Lexical, always apply marks\n return true;\n }\n catch (error) {\n Console.catch('[shouldApplyMark] Error checking persistVeltMarks, defaulting to true', error);\n return true;\n }\n};\n"],"names":["Constants","ATTRIBUTES","EDITOR_ID","LOCATION_ID","ANNOTATION_ID","MULTI_THREAD_ANNOTATION_ID","MARK_NAME","STORAGE_KEY","TAG_NAME","DEFAULT_EXTENSION_NAME","NODE_TYPE","DEFAULT_EDITOR_ID","STATUS_TERMINAL","LOG_PREFIX","DEBUG_MODE_KEY","FORCE_DEBUG_MODE_KEY","BLOCK_TYPES","Console","showLogs","sessionStorage","getItem","err","catch","log","console","warn","error","debug","info","logsEnabled","message","undefined","kmpSearch","text","pattern","startPos","maxOccurrences","positions","length","lps","computeKMPTable","i","j","push","Array","fill","len","collectTextContent","root","nodeMap","combinedText","currentOffset","traverse","node","depth","$isTextNode","getTextContent","startOffset","endOffset","start","end","$isElementNode","children","getChildren","nodeType","getType","includes","parent","getParent","siblings","indexOf","isParagraphBreak","getContainerTextContent","containerElement","editor","containerStartOffset","containerEndOffset","item","nodeKey","getKey","nodeDOMElement","getElementByKey","contains","substring","CommentNode","ElementNode","clone","__annotationId","__multiThreadAnnotationId","__key","constructor","annotationId","multiThreadAnnotationId","key","super","this","createDOM","config","dom","document","createElement","setAttribute","updateDOM","_prevNode","_dom","_config","importJSON","serializedNode","$createCommentNode","exportJSON","type","version","isInline","$isCommentNode","isRangeAlreadyWrapped","fromPos","toPos","currentPosition","foundWrapping","nodeStart","nodeEnd","child","getTextNodesInRange","excludeAnnotationId","textNodes","Math","max","min","isPartial","findParagraphParent","blockTypes","current","wrapPartialTextNode","textNode","commentNode","fullText","format","getFormat","style","getStyle","nodeIndex","getIndexWithinParent","beforeText","selectedText","afterText","selectedTextNode","$createTextNode","setFormat","setStyle","append","nodesToInsert","beforeNode","afterNode","getWritable","splice","wrapEntireTextNode","writableNode","wrapMultipleTextNodesWithFormatting","textNodesToWrap","logPrefix","nodesByParagraph","Map","nodeData","paragraphParent","paragraphKey","has","set","get","isFirstParagraph","paragraphIndex","nodesInParagraph","entries","paragraphCommentNode","wrapMultipleNodesInSameParagraph","sortedNodes","sort","a","b","firstNodeData","lastNodeData","originalParent","originalIndex","parentChildrenCount","getChildrenSize","firstNode","parentWritable","insertionIndex","selectedPartNode","currentSize","lastNode","replace","findTextMatches","annotation","matches","context","textEditorConfig","getEditorState","read","$getRoot","pos","applyAnnotationMarks","annotations","annotationLogPrefix","desiredOccurrence","occurrence","targetTextNodeId","domNode","getElementById","containerContent","map","match","update","$getSelection","$setSelection","discrete","updateError","getCurrentSelectionContext","selection","$isRangeSelection","trim","range","anchorNode","anchor","getNode","focusNode","focus","anchorOffset","offset","focusOffset","anchorTextPos","focusTextPos","getSelectionTextRange","locationId","rootElement","getRootElement","anchorKey","anchorDOMNode","currentNode","MAX_ITERATIONS","iterationCount","body","id","parentElement","locationElement","closest","getAttribute","findOccurrencesByText","from","to","subscribeToDocumentChanges","callback","prevText","registerUpdateListener","editorState","prevEditorState","updateLogPrefix","docChanged","currentState","prevState","currentText","hasDocumentChanged","event","insertedText","deletedText","detectContainerChanges","params","targetTextNodeIdChanged","newTargetTextNodeId","searchResults","newContainer","findNewContainer","editorElement","elementsWithIds","querySelectorAll","element","textContent","calculateNewOccurrence","originalOccurrence","nodes","annotationRange","nodeSize","result","isSubscribed","selectedCommentsMap","subscribers","getCommentElement","velt","window","Velt","updateAnnotationContext","commentElement","updateContext","stateMap","createAnnotationState","editorId","state","commentAnnotations","selectedAnnotations","Set","getOrCreateState","updateAnnotations","clear","annotationData","getAnnotation","registry","ensureEditorRegistered","finalEditorId","namespace","getEditorId","existing","getEditorContext","registerEditor","handleContentUpdate","transaction","changes","getAnnotationData","annotationsToCheck","commentNodeCount","existingAnnotation","data","storedData","n","join","contentChanged","originalText","containerResult","currentOccurrence","occurrenceChanged","change","originalTargetTextNodeId","detectDocumentChanges","size","changeLogPrefix","JSON","parse","stringify","updatedContext","documentChangeSubscriptions","ensureEditorSetup","unsubscribeDocChanges","ensureDocumentChangeListeners","rendererStore","convertToAnnotationData","filteredAnnotations","existingAnnotations","getAllAnnotations","existingMap","ann","annotationDataArray","updateComments","storeEntry","previousFilteredAnnotations","selectedIds","getSelectedAnnotations","filter","status","filterAnnotations","newAnnotationIds","Boolean","previousAnnotationIds","removedAnnotationIds","annotationIds","commentNodesToRemove","firstChild","insertAfter","remove","nodeError","removeAnnotationMarks","currentStoreEntry","updateSelection","getCommentAnnotations","subscriptionMap","COMMENT_TYPE","stripCommentsFromRoot","nextChildren","stripFromChildrenArray","out","stripCommentsFromNode","isElementNode","flattened","normalizeTextNodesInRoot","normalizedKids","normalizeTextNodesInNode","mergeAdjacentTextNodes","merged","cur","isTextNode","nxt","haveSameTextFormat","Object","prototype","hasOwnProperty","call","mode","detail","async","clientContext","selectionContext","userProvidedEditorId","location","veltResult","addManualComment","then","Promise","resolve","addVeltComment","shouldApplyMark","lexicalEditor","position","applyAnnotationMark","editorWithFocus","resetEditorSelection","raw","toJSON","strippedRoot","isArray","editorAnnotations","annotationEditorId","editorIdForSubscription","unsubscribe","subscriberId","Date","now","random","getSelectedComments","subscribe","comments","newSelectedMap","forEach","comment","map1","map2","value","areMapsEqual","add","subscriber","delete","subscribeToSelectedAnnotations","setSelectedAnnotations"],"mappings":"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"}