mirror of https://github.com/usememos/memos.git
fix: cursor position after slash commands
This commit is contained in:
parent
15646a8989
commit
4b110d0d38
|
|
@ -1,21 +1,26 @@
|
|||
import type { SlashCommandsProps } from "../types";
|
||||
import type { EditorRefActions } from ".";
|
||||
import { SuggestionsPopup } from "./SuggestionsPopup";
|
||||
import { useSuggestions } from "./useSuggestions";
|
||||
|
||||
const SlashCommands = ({ editorRef, editorActions, commands }: SlashCommandsProps) => {
|
||||
const handleCommandAutocomplete = (cmd: (typeof commands)[0], word: string, index: number, actions: EditorRefActions) => {
|
||||
// Remove trigger char + word, then insert command output
|
||||
actions.removeText(index, word.length);
|
||||
actions.insertText(cmd.run());
|
||||
// Position cursor relative to insertion point, if specified
|
||||
if (cmd.cursorOffset) {
|
||||
actions.setCursorPosition(index + cmd.cursorOffset);
|
||||
}
|
||||
};
|
||||
|
||||
const { position, suggestions, selectedIndex, isVisible, handleItemSelect } = useSuggestions({
|
||||
editorRef,
|
||||
editorActions,
|
||||
triggerChar: "/",
|
||||
items: commands,
|
||||
filterItems: (items, query) => (!query ? items : items.filter((cmd) => cmd.name.toLowerCase().startsWith(query))),
|
||||
onAutocomplete: (cmd, word, index, actions) => {
|
||||
actions.removeText(index, word.length);
|
||||
actions.insertText(cmd.run());
|
||||
if (cmd.cursorOffset) {
|
||||
actions.setCursorPosition(actions.getCursorPosition() + cmd.cursorOffset);
|
||||
}
|
||||
},
|
||||
onAutocomplete: handleCommandAutocomplete,
|
||||
});
|
||||
|
||||
if (!isVisible || !position) return null;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ const Editor = forwardRef(function Editor(props: EditorProps, ref: React.Forward
|
|||
|
||||
editor.value = editor.value.slice(0, start) + editor.value.slice(start + length);
|
||||
editor.focus();
|
||||
editor.selectionEnd = start;
|
||||
editor.setSelectionRange(start, start);
|
||||
updateContent();
|
||||
},
|
||||
setContent: (text: string) => {
|
||||
|
|
@ -116,8 +116,11 @@ const Editor = forwardRef(function Editor(props: EditorProps, ref: React.Forward
|
|||
return editor.value.slice(editor.selectionStart, editor.selectionEnd);
|
||||
},
|
||||
setCursorPosition: (startPos: number, endPos?: number) => {
|
||||
const endPosition = Number.isNaN(endPos) ? startPos : (endPos as number);
|
||||
editorRef.current?.setSelectionRange(startPos, endPosition);
|
||||
const editor = editorRef.current;
|
||||
if (!editor) return;
|
||||
// setSelectionRange requires valid arguments; default to startPos if endPos is undefined
|
||||
const endPosition = endPos !== undefined && !Number.isNaN(endPos) ? endPos : startPos;
|
||||
editor.setSelectionRange(startPos, endPosition);
|
||||
},
|
||||
getCursorLineNumber: () => {
|
||||
const editor = editorRef.current;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export function useSuggestions<T>({
|
|||
}: UseSuggestionsOptions<T>): UseSuggestionsReturn<T> {
|
||||
const [position, setPosition] = useState<Position | null>(null);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const isProcessingRef = useRef(false);
|
||||
|
||||
const selectedRef = useRef(selectedIndex);
|
||||
selectedRef.current = selectedIndex;
|
||||
|
|
@ -66,9 +67,14 @@ export function useSuggestions<T>({
|
|||
console.warn("useSuggestions: editorActions not available");
|
||||
return;
|
||||
}
|
||||
isProcessingRef.current = true;
|
||||
const [word, index] = getCurrentWord();
|
||||
onAutocomplete(item, word, index, editorActions.current);
|
||||
hide();
|
||||
// Re-enable input handling after all DOM operations complete
|
||||
queueMicrotask(() => {
|
||||
isProcessingRef.current = false;
|
||||
});
|
||||
};
|
||||
|
||||
const handleNavigation = (e: KeyboardEvent, selected: number, suggestionsCount: number) => {
|
||||
|
|
@ -107,6 +113,8 @@ export function useSuggestions<T>({
|
|||
};
|
||||
|
||||
const handleInput = () => {
|
||||
if (isProcessingRef.current) return;
|
||||
|
||||
const editor = editorRef.current;
|
||||
if (!editor) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ const AttachmentList = ({ attachments }: AttachmentListProps) => {
|
|||
<div className="p-2 flex flex-col gap-1">
|
||||
{mediaItems.length > 0 && <MediaGrid attachments={mediaItems} onImageClick={handleImageClick} />}
|
||||
|
||||
{mediaItems.length > 0 && docItems.length > 0 && <div className="border-t border-border opacity-60" />}
|
||||
{mediaItems.length > 0 && docItems.length > 0 && <div className="border-t mt-1 border-border opacity-60" />}
|
||||
|
||||
{docItems.length > 0 && <DocsList attachments={docItems} />}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue