diff --git a/web/src/components/MemoEditor/Editor/SlashCommands.tsx b/web/src/components/MemoEditor/Editor/SlashCommands.tsx index 883225be0..b00bbd008 100644 --- a/web/src/components/MemoEditor/Editor/SlashCommands.tsx +++ b/web/src/components/MemoEditor/Editor/SlashCommands.tsx @@ -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; diff --git a/web/src/components/MemoEditor/Editor/index.tsx b/web/src/components/MemoEditor/Editor/index.tsx index 2f684c3ed..84ad32c2f 100644 --- a/web/src/components/MemoEditor/Editor/index.tsx +++ b/web/src/components/MemoEditor/Editor/index.tsx @@ -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; diff --git a/web/src/components/MemoEditor/Editor/useSuggestions.ts b/web/src/components/MemoEditor/Editor/useSuggestions.ts index c11d01ff4..aff69a149 100644 --- a/web/src/components/MemoEditor/Editor/useSuggestions.ts +++ b/web/src/components/MemoEditor/Editor/useSuggestions.ts @@ -35,6 +35,7 @@ export function useSuggestions({ }: UseSuggestionsOptions): UseSuggestionsReturn { const [position, setPosition] = useState(null); const [selectedIndex, setSelectedIndex] = useState(0); + const isProcessingRef = useRef(false); const selectedRef = useRef(selectedIndex); selectedRef.current = selectedIndex; @@ -66,9 +67,14 @@ export function useSuggestions({ 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({ }; const handleInput = () => { + if (isProcessingRef.current) return; + const editor = editorRef.current; if (!editor) return; diff --git a/web/src/components/MemoView/components/metadata/AttachmentList.tsx b/web/src/components/MemoView/components/metadata/AttachmentList.tsx index c60db187f..1ea2ecf9b 100644 --- a/web/src/components/MemoView/components/metadata/AttachmentList.tsx +++ b/web/src/components/MemoView/components/metadata/AttachmentList.tsx @@ -120,7 +120,7 @@ const AttachmentList = ({ attachments }: AttachmentListProps) => {
{mediaItems.length > 0 && } - {mediaItems.length > 0 && docItems.length > 0 &&
} + {mediaItems.length > 0 && docItems.length > 0 &&
} {docItems.length > 0 && }