Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
4b110d0d
Commit
4b110d0d
authored
Jan 05, 2026
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: cursor position after slash commands
parent
15646a89
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
27 additions
and
11 deletions
+27
-11
SlashCommands.tsx
web/src/components/MemoEditor/Editor/SlashCommands.tsx
+12
-7
index.tsx
web/src/components/MemoEditor/Editor/index.tsx
+6
-3
useSuggestions.ts
web/src/components/MemoEditor/Editor/useSuggestions.ts
+8
-0
AttachmentList.tsx
...omponents/MemoView/components/metadata/AttachmentList.tsx
+1
-1
No files found.
web/src/components/MemoEditor/Editor/SlashCommands.tsx
View file @
4b110d0d
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
;
...
...
web/src/components/MemoEditor/Editor/index.tsx
View file @
4b110d0d
...
...
@@ -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.se
lectionEnd = start
;
editor.se
tSelectionRange(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;
...
...
web/src/components/MemoEditor/Editor/useSuggestions.ts
View file @
4b110d0d
...
...
@@ -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
;
...
...
web/src/components/MemoView/components/metadata/AttachmentList.tsx
View file @
4b110d0d
...
...
@@ -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
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment