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
}
});