@@ -6,7 +6,7 @@ const mdToFormattingType: Record<string, MessageInputFormattingType> = {
66 '**' : 'bold' ,
77 '*' : 'italics' ,
88 '~~' : 'strikethrough' ,
9- '`` ' : 'code' ,
9+ '`' : 'code' ,
1010}
1111
1212export const formattingTypeToMarkdown : Record < MessageInputFormattingType , string > = {
@@ -47,29 +47,36 @@ export const useMessageInputCompositionControls = () => {
4747 const textarea = textareaRef . current ;
4848 if ( ! textarea ) return ;
4949
50+ let newSelection ;
5051 const { activeFormatting} = customDataManager . customComposerData ;
5152 if ( ! activeFormatting ) {
5253 textComposer . wrapSelection ( { head : wrappingMarkdown , tail : wrappingMarkdown } ) ;
5354 customDataManager . setCustomData ( { activeFormatting : mdToFormattingType [ wrappingMarkdown ] } ) ;
54- textarea . selectionStart = textComposer . selection . start ;
55- textarea . selectionEnd = textComposer . selection . end ;
56- textarea . focus ( ) ;
57- return ;
58- }
59- const activeMarkdown = formattingTypeToMarkdown [ activeFormatting ] ;
60- const newSelection = {
61- start : textComposer . selection . start + activeMarkdown . length ,
62- end : textComposer . selection . end + + activeMarkdown . length ,
63- } ;
64- textarea . selectionStart = newSelection . start ;
65- textarea . selectionEnd = newSelection . end ;
66- if ( wrappingMarkdown === activeMarkdown ) {
67- customDataManager . setCustomData ( { activeFormatting : null } ) ;
55+ newSelection = {
56+ start :textComposer . selection . start ,
57+ end : textComposer . selection . end ,
58+ } ;
6859 } else {
69- customDataManager . setCustomData ( { activeFormatting : mdToFormattingType [ wrappingMarkdown ] } ) ;
70- textComposer . wrapSelection ( { head : wrappingMarkdown , selection : newSelection , tail : wrappingMarkdown } ) ;
60+ const activeMarkdown = formattingTypeToMarkdown [ activeFormatting ] ;
61+ newSelection = {
62+ start : textComposer . selection . start + activeMarkdown . length + wrappingMarkdown . length ,
63+ end : textComposer . selection . end + + activeMarkdown . length + wrappingMarkdown . length ,
64+ } ;
65+ if ( wrappingMarkdown === activeMarkdown ) {
66+ customDataManager . setCustomData ( { activeFormatting : null } ) ;
67+ } else {
68+ customDataManager . setCustomData ( { activeFormatting : mdToFormattingType [ wrappingMarkdown ] } ) ;
69+ textComposer . wrapSelection ( { head : wrappingMarkdown , selection : newSelection , tail : wrappingMarkdown } ) ;
70+ }
7171 }
7272 textarea . focus ( ) ;
73+ /**
74+ * Some browsers (especially Chrome/Edge/WebKit) will move the caret to the end of the text after focus as part of their default focus-handling.
75+ * That happens after our JS runs, so it overwrites the position we set - browser invokes the event with the selection at the end of the textarea string
76+ */
77+ setTimeout ( ( ) => {
78+ textarea . setSelectionRange ( newSelection . start , newSelection . end )
79+ } , 0 ) ;
7380 } , [ customDataManager , textareaRef , textComposer ] )
7481
7582 const formatter = useMemo < Record < MessageInputFormattingType , ( ) => void > > ( ( ) => ( {
0 commit comments