mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-21 03:10:50 +00:00
V4.12.3 features (#5595)
* refactor: remove ModelProviderIdType and update related types (#5549) * perf: model provider * fix eval create split (#5570) * git rebase --continuedoc * add more variable types (#5540) * variable types * password * time picker * internal var * file * fix-test * time select default value & range * password & type render * fix * fix build * fix * move method * split date select * icon * perf: variable code * prompt editor add markdown plugin (#5556) * editor markdown * fix build * pnpm lock * add props * update code * fix list * editor ui * fix variable reset (#5586) * perf: variables type code * customize lexical indent (#5588) * perf: multiple selector * perf: tab plugin * doc * refactor: update workflow constants to use ToolTypeEnum (#5491) * refactor: replace FlowNodeTemplateTypeEnum with string literals in workflow templates * perf: tool type --------- Co-authored-by: archer <545436317@qq.com> * update doc * fix: make table's row more natural while dragging it (#5596) * feat: add APIGetTemplate function and refactor template fetching logic (#5498) * feat: add APIGetTemplate function and refactor template fetching logic * chore: adjust the code * chore: update sdk --------- Co-authored-by: FinleyGe <m13203533462@163.com> * perf init system * doc * remove log * remove i18n * perf: variables render --------- Co-authored-by: Ctrlz <143257420+ctrlz526@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com> Co-authored-by: FinleyGe <m13203533462@163.com>
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
import {
|
||||
KEY_TAB_COMMAND,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
$isTextNode
|
||||
} from 'lexical';
|
||||
import { $createTextNode } from 'lexical';
|
||||
import { $isListNode, $isListItemNode } from '@lexical/list';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function TabToSpacesPlugin(): null {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
useEffect(() => {
|
||||
return editor.registerCommand(
|
||||
KEY_TAB_COMMAND,
|
||||
(event) => {
|
||||
try {
|
||||
const selection = $getSelection();
|
||||
if (!$isRangeSelection(selection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we're in a list context
|
||||
let isInList = false;
|
||||
try {
|
||||
const nodes = selection.getNodes();
|
||||
isInList = nodes.some((node) => {
|
||||
// Check if current node or any of its ancestors is a list or list item
|
||||
let currentNode = node;
|
||||
while (currentNode) {
|
||||
try {
|
||||
if ($isListNode(currentNode) || $isListItemNode(currentNode)) {
|
||||
return true;
|
||||
}
|
||||
// @ts-ignore
|
||||
currentNode = currentNode.getParent();
|
||||
} catch (e) {
|
||||
// If node is no longer valid, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} catch (e) {
|
||||
// If we can't get nodes safely, assume we're not in a list
|
||||
isInList = false;
|
||||
}
|
||||
|
||||
// If we're in a list, let the built-in list indentation handle it
|
||||
if (isInList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only handle tab for non-list contexts
|
||||
event.preventDefault();
|
||||
|
||||
const isShiftTab = event.shiftKey;
|
||||
|
||||
// Handle Shift+Tab (outdent)
|
||||
if (isShiftTab) {
|
||||
if (!selection.isCollapsed()) {
|
||||
// For selected text, remove 4 spaces from the beginning of each line
|
||||
try {
|
||||
const selectedText = selection.getTextContent();
|
||||
const lines = selectedText.split('\n');
|
||||
|
||||
const outdentedText = lines
|
||||
.map((line) => {
|
||||
// Remove up to 4 spaces from the beginning of the line
|
||||
if (line.startsWith(' ')) {
|
||||
return line.slice(4);
|
||||
} else if (line.startsWith(' ')) {
|
||||
return line.slice(3);
|
||||
} else if (line.startsWith(' ')) {
|
||||
return line.slice(2);
|
||||
} else if (line.startsWith(' ')) {
|
||||
return line.slice(1);
|
||||
}
|
||||
return line;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
// Insert the outdented text and let Lexical handle cursor positioning
|
||||
selection.insertText(outdentedText);
|
||||
|
||||
// Schedule selection restoration in the next update cycle
|
||||
setTimeout(() => {
|
||||
editor.update(() => {
|
||||
const currentSelection = $getSelection();
|
||||
if ($isRangeSelection(currentSelection) && !currentSelection.isCollapsed()) {
|
||||
// Selection is already maintained, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// If selection was lost, try to select the inserted text
|
||||
if ($isRangeSelection(currentSelection)) {
|
||||
const currentOffset = currentSelection.anchor.offset;
|
||||
const selectionStart = Math.max(0, currentOffset - outdentedText.length);
|
||||
|
||||
currentSelection.anchor.set(
|
||||
currentSelection.anchor.key,
|
||||
selectionStart,
|
||||
'text'
|
||||
);
|
||||
currentSelection.focus.set(currentSelection.focus.key, currentOffset, 'text');
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
// If operation fails, do nothing
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// For cursor position, try to remove spaces before cursor
|
||||
try {
|
||||
const anchorNode = selection.anchor.getNode();
|
||||
const anchorOffset = selection.anchor.offset;
|
||||
|
||||
if ($isTextNode(anchorNode)) {
|
||||
const textContent = anchorNode.getTextContent();
|
||||
const beforeCursor = textContent.slice(0, anchorOffset);
|
||||
const afterCursor = textContent.slice(anchorOffset);
|
||||
|
||||
// Check if there are spaces before cursor to remove
|
||||
let spacesToRemove = 0;
|
||||
for (let i = beforeCursor.length - 1; i >= 0 && spacesToRemove < 4; i--) {
|
||||
if (beforeCursor[i] === ' ') {
|
||||
spacesToRemove++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (spacesToRemove > 0) {
|
||||
const newTextContent =
|
||||
beforeCursor.slice(0, beforeCursor.length - spacesToRemove) + afterCursor;
|
||||
anchorNode.setTextContent(newTextContent);
|
||||
selection.anchor.set(
|
||||
anchorNode.getKey(),
|
||||
anchorOffset - spacesToRemove,
|
||||
'text'
|
||||
);
|
||||
selection.focus.set(anchorNode.getKey(), anchorOffset - spacesToRemove, 'text');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle regular Tab (indent)
|
||||
if (!selection.isCollapsed()) {
|
||||
try {
|
||||
const selectedText = selection.getTextContent();
|
||||
const lines = selectedText.split('\n');
|
||||
const indentedText = lines.map((line) => ' ' + line).join('\n');
|
||||
|
||||
// Insert the indented text and let Lexical handle cursor positioning
|
||||
selection.insertText(indentedText);
|
||||
|
||||
// Schedule selection restoration in the next update cycle
|
||||
setTimeout(() => {
|
||||
editor.update(() => {
|
||||
const currentSelection = $getSelection();
|
||||
if ($isRangeSelection(currentSelection) && !currentSelection.isCollapsed()) {
|
||||
// Selection is already maintained, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// If selection was lost, try to select the inserted text
|
||||
if ($isRangeSelection(currentSelection)) {
|
||||
const currentOffset = currentSelection.anchor.offset;
|
||||
const selectionStart = Math.max(0, currentOffset - indentedText.length);
|
||||
|
||||
currentSelection.anchor.set(
|
||||
currentSelection.anchor.key,
|
||||
selectionStart,
|
||||
'text'
|
||||
);
|
||||
currentSelection.focus.set(currentSelection.focus.key, currentOffset, 'text');
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
// If selection operation fails, fall back to simple space insertion
|
||||
const textNode = $createTextNode(' ');
|
||||
selection.insertNodes([textNode]);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// For cursor position (no selection), insert 4 spaces
|
||||
const textNode = $createTextNode(' '); // 4 spaces
|
||||
selection.insertNodes([textNode]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If anything fails, just let the default behavior handle it
|
||||
console.warn('TabToSpacesPlugin error:', e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR
|
||||
);
|
||||
}, [editor]);
|
||||
|
||||
return null;
|
||||
}
|
Reference in New Issue
Block a user