Skip to content

Commit 4b110d0

Browse files
committed
fix: cursor position after slash commands
1 parent 15646a8 commit 4b110d0

File tree

4 files changed

+27
-11
lines changed

4 files changed

+27
-11
lines changed

web/src/components/MemoEditor/Editor/SlashCommands.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
import type { SlashCommandsProps } from "../types";
2+
import type { EditorRefActions } from ".";
23
import { SuggestionsPopup } from "./SuggestionsPopup";
34
import { useSuggestions } from "./useSuggestions";
45

56
const SlashCommands = ({ editorRef, editorActions, commands }: SlashCommandsProps) => {
7+
const handleCommandAutocomplete = (cmd: (typeof commands)[0], word: string, index: number, actions: EditorRefActions) => {
8+
// Remove trigger char + word, then insert command output
9+
actions.removeText(index, word.length);
10+
actions.insertText(cmd.run());
11+
// Position cursor relative to insertion point, if specified
12+
if (cmd.cursorOffset) {
13+
actions.setCursorPosition(index + cmd.cursorOffset);
14+
}
15+
};
16+
617
const { position, suggestions, selectedIndex, isVisible, handleItemSelect } = useSuggestions({
718
editorRef,
819
editorActions,
920
triggerChar: "/",
1021
items: commands,
1122
filterItems: (items, query) => (!query ? items : items.filter((cmd) => cmd.name.toLowerCase().startsWith(query))),
12-
onAutocomplete: (cmd, word, index, actions) => {
13-
actions.removeText(index, word.length);
14-
actions.insertText(cmd.run());
15-
if (cmd.cursorOffset) {
16-
actions.setCursorPosition(actions.getCursorPosition() + cmd.cursorOffset);
17-
}
18-
},
23+
onAutocomplete: handleCommandAutocomplete,
1924
});
2025

2126
if (!isVisible || !position) return null;

web/src/components/MemoEditor/Editor/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const Editor = forwardRef(function Editor(props: EditorProps, ref: React.Forward
9898

9999
editor.value = editor.value.slice(0, start) + editor.value.slice(start + length);
100100
editor.focus();
101-
editor.selectionEnd = start;
101+
editor.setSelectionRange(start, start);
102102
updateContent();
103103
},
104104
setContent: (text: string) => {
@@ -116,8 +116,11 @@ const Editor = forwardRef(function Editor(props: EditorProps, ref: React.Forward
116116
return editor.value.slice(editor.selectionStart, editor.selectionEnd);
117117
},
118118
setCursorPosition: (startPos: number, endPos?: number) => {
119-
const endPosition = Number.isNaN(endPos) ? startPos : (endPos as number);
120-
editorRef.current?.setSelectionRange(startPos, endPosition);
119+
const editor = editorRef.current;
120+
if (!editor) return;
121+
// setSelectionRange requires valid arguments; default to startPos if endPos is undefined
122+
const endPosition = endPos !== undefined && !Number.isNaN(endPos) ? endPos : startPos;
123+
editor.setSelectionRange(startPos, endPosition);
121124
},
122125
getCursorLineNumber: () => {
123126
const editor = editorRef.current;

web/src/components/MemoEditor/Editor/useSuggestions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function useSuggestions<T>({
3535
}: UseSuggestionsOptions<T>): UseSuggestionsReturn<T> {
3636
const [position, setPosition] = useState<Position | null>(null);
3737
const [selectedIndex, setSelectedIndex] = useState(0);
38+
const isProcessingRef = useRef(false);
3839

3940
const selectedRef = useRef(selectedIndex);
4041
selectedRef.current = selectedIndex;
@@ -66,9 +67,14 @@ export function useSuggestions<T>({
6667
console.warn("useSuggestions: editorActions not available");
6768
return;
6869
}
70+
isProcessingRef.current = true;
6971
const [word, index] = getCurrentWord();
7072
onAutocomplete(item, word, index, editorActions.current);
7173
hide();
74+
// Re-enable input handling after all DOM operations complete
75+
queueMicrotask(() => {
76+
isProcessingRef.current = false;
77+
});
7278
};
7379

7480
const handleNavigation = (e: KeyboardEvent, selected: number, suggestionsCount: number) => {
@@ -107,6 +113,8 @@ export function useSuggestions<T>({
107113
};
108114

109115
const handleInput = () => {
116+
if (isProcessingRef.current) return;
117+
110118
const editor = editorRef.current;
111119
if (!editor) return;
112120

web/src/components/MemoView/components/metadata/AttachmentList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const AttachmentList = ({ attachments }: AttachmentListProps) => {
120120
<div className="p-2 flex flex-col gap-1">
121121
{mediaItems.length > 0 && <MediaGrid attachments={mediaItems} onImageClick={handleImageClick} />}
122122

123-
{mediaItems.length > 0 && docItems.length > 0 && <div className="border-t border-border opacity-60" />}
123+
{mediaItems.length > 0 && docItems.length > 0 && <div className="border-t mt-1 border-border opacity-60" />}
124124

125125
{docItems.length > 0 && <DocsList attachments={docItems} />}
126126
</div>

0 commit comments

Comments
 (0)