diff --git a/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx b/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx index 152083f4..d3570b0f 100644 --- a/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx +++ b/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx @@ -1,6 +1,6 @@ import { useEffect, useState, type FC } from 'react'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { $getRoot, $getSelection } from 'lexical'; +import { $getSelection, $isRangeSelection } from 'lexical'; import { INSERT_VARIABLE_COMMAND } from '../commands'; import type { NodeProperties } from '../../../types' @@ -23,43 +23,55 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => { useEffect(() => { return editor.registerUpdateListener(({ editorState }) => { editorState.read(() => { - const root = $getRoot(); - const text = root.getTextContent(); - const shouldShow = text.includes('/'); + const selection = $getSelection(); + + if (!selection || !$isRangeSelection(selection)) { + setShowSuggestions(false); + return; + } + + const anchorNode = selection.anchor.getNode(); + const anchorOffset = selection.anchor.offset; + + // Get the text content of the current node + const nodeText = anchorNode.getTextContent(); + + // Check if we have a '/' at the current position or after line break + const textBeforeCursor = nodeText.substring(0, anchorOffset); + const shouldShow = textBeforeCursor.endsWith('/') || + (textBeforeCursor === '/' && anchorOffset === 1); + setShowSuggestions(shouldShow); if (shouldShow) { - const selection = $getSelection(); - if (selection) { - const domSelection = window.getSelection(); - if (domSelection && domSelection.rangeCount > 0) { - const range = domSelection.getRangeAt(0); - const rect = range.getBoundingClientRect(); + const domSelection = window.getSelection(); + if (domSelection && domSelection.rangeCount > 0) { + const range = domSelection.getRangeAt(0); + const rect = range.getBoundingClientRect(); - const popupWidth = 280; - const popupHeight = 200; - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + const popupWidth = 280; + const popupHeight = 200; + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - let left = rect.left; - let top = rect.top - 10; + let left = rect.left; + let top = rect.top - 10; - if (left + popupWidth > viewportWidth) { - left = viewportWidth - popupWidth - 10; - } - if (left < 10) { - left = 10; - } - - if (top - popupHeight < 10) { - top = rect.bottom + 10; - if (top + popupHeight > viewportHeight) { - top = viewportHeight - popupHeight - 10; - } - } - - setPopupPosition({ top, left }); + if (left + popupWidth > viewportWidth) { + left = viewportWidth - popupWidth - 10; } + if (left < 10) { + left = 10; + } + + if (top - popupHeight < 10) { + top = rect.bottom + 10; + if (top + popupHeight > viewportHeight) { + top = viewportHeight - popupHeight - 10; + } + } + + setPopupPosition({ top, left }); } } }); @@ -112,7 +124,7 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => {
{nodeName}
- {nodeOptions.map((option, index) => { + {nodeOptions.map((option) => { const globalIndex = Object.values(groupedSuggestions).flat().indexOf(option); return (
{ INSERT_VARIABLE_COMMAND, (payload: InsertVariableCommandPayload) => { editor.update(() => { - const root = $getRoot(); - const text = root.getTextContent(); - const lastSlashIndex = text.lastIndexOf('/'); + const selection = $getSelection(); + if (!selection || !$isRangeSelection(selection)) return; - // Find the paragraph and the position to insert - const paragraph = root.getFirstChild(); - if (!paragraph || !$isParagraphNode(paragraph)) return; + const anchorNode = selection.anchor.getNode(); + const anchorOffset = selection.anchor.offset; - const children = paragraph.getChildren(); - let insertPosition = 0; - let currentTextLength = 0; - - // Find where to insert the new tag - for (let i = 0; i < children.length; i++) { - const child = children[i]; - const childText = child.getTextContent(); + if ($isTextNode(anchorNode)) { + const nodeText = anchorNode.getTextContent(); + const textBeforeCursor = nodeText.substring(0, anchorOffset); + const textAfterCursor = nodeText.substring(anchorOffset); - if (currentTextLength + childText.length > lastSlashIndex) { - // Split this text node if needed - if ($isTextNode(child)) { - const beforeSlash = childText.substring(0, lastSlashIndex - currentTextLength); - const afterSlash = childText.substring(lastSlashIndex - currentTextLength + 1); - - if (beforeSlash) { - child.setTextContent(beforeSlash); - insertPosition = i + 1; - } else { - insertPosition = i; - child.remove(); - } - - // Insert tag and space - const tagNode = $createVariableNode(payload.data); - const spaceNode = $createTextNode(' '); - - if (insertPosition < paragraph.getChildrenSize()) { - paragraph.getChildAtIndex(insertPosition)?.insertBefore(tagNode); - tagNode.insertAfter(spaceNode); - } else { - paragraph.append(tagNode); - paragraph.append(spaceNode); - } - - if (afterSlash) { - spaceNode.insertAfter($createTextNode(afterSlash)); - } - - // Set cursor after space - const selection = $createRangeSelection(); - selection.anchor.set(spaceNode.getKey(), 1, 'text'); - selection.focus.set(spaceNode.getKey(), 1, 'text'); - $setSelection(selection); + // Find the last '/' position + const lastSlashIndex = textBeforeCursor.lastIndexOf('/'); + + if (lastSlashIndex !== -1) { + // Split the text: before slash, insert variable, after cursor + const beforeSlash = textBeforeCursor.substring(0, lastSlashIndex); + + // Update the current text node with text before slash + anchorNode.setTextContent(beforeSlash); + + // Create and insert the variable node + const tagNode = $createVariableNode(payload.data); + const spaceNode = $createTextNode(' '); + + anchorNode.insertAfter(tagNode); + tagNode.insertAfter(spaceNode); + + // Add remaining text if any + if (textAfterCursor) { + spaceNode.insertAfter($createTextNode(textAfterCursor)); } - break; + + // Set cursor after space + const newSelection = $createRangeSelection(); + newSelection.anchor.set(spaceNode.getKey(), 1, 'text'); + newSelection.focus.set(spaceNode.getKey(), 1, 'text'); + $setSelection(newSelection); } - - currentTextLength += childText.length; - insertPosition = i + 1; } }); return true; diff --git a/web/src/views/Workflow/components/Properties/index.tsx b/web/src/views/Workflow/components/Properties/index.tsx index be4405db..ec2116cb 100644 --- a/web/src/views/Workflow/components/Properties/index.tsx +++ b/web/src/views/Workflow/components/Properties/index.tsx @@ -237,6 +237,20 @@ const Properties: FC = ({ }); } break + case 'knowledge-retrieval': + const knowledgeKey = `${nodeId}_message`; + if (!addedKeys.has(knowledgeKey)) { + addedKeys.add(knowledgeKey); + variableList.push({ + key: knowledgeKey, + label: 'message', + type: 'variable', + dataType: 'String', + value: `${nodeId}.message`, + nodeData: nodeData, + }); + } + break } });