@@ -153,6 +153,40 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
153153 }
154154 } ;
155155
156+ const insertPlainText = ( plainText : string , target : HTMLElement ) => {
157+ const selection = window . getSelection ( ) ;
158+ if ( ! selection || selection . rangeCount === 0 ) {
159+ return ;
160+ }
161+
162+ const range = selection . getRangeAt ( 0 ) ;
163+ if ( ! target . contains ( range . commonAncestorContainer ) ) {
164+ target . focus ( ) ;
165+ range . selectNodeContents ( target ) ;
166+ range . collapse ( false ) ;
167+ selection . removeAllRanges ( ) ;
168+ selection . addRange ( range ) ;
169+ }
170+ range . deleteContents ( ) ;
171+ range . insertNode ( document . createTextNode ( plainText ) ) ;
172+ } ;
173+
174+ const handlePaste = ( event : React . ClipboardEvent < HTMLSpanElement > ) => {
175+ event . preventDefault ( ) ;
176+ insertPlainText (
177+ event . clipboardData . getData ( 'text/plain' ) ,
178+ event . currentTarget ,
179+ ) ;
180+ } ;
181+
182+ const handleDrop = ( event : React . DragEvent < HTMLSpanElement > ) => {
183+ event . preventDefault ( ) ;
184+ insertPlainText (
185+ event . dataTransfer . getData ( 'text/plain' ) ,
186+ event . currentTarget ,
187+ ) ;
188+ } ;
189+
156190 useEffect ( ( ) => {
157191 setTitleDisplay ( isTopRoot ? doc . title : titleWithoutEmoji ) ;
158192 } , [ doc . title , isTopRoot , titleWithoutEmoji ] ) ;
@@ -181,6 +215,8 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
181215 onBlurCapture = { ( event ) =>
182216 handleTitleSubmit ( event . target . textContent || '' )
183217 }
218+ onPasteCapture = { handlePaste }
219+ onDropCapture = { handleDrop }
184220 $padding = { { right : 'big' } }
185221 $css = { css `
186222 & [contenteditable = 'true' ]: empty : not (: focus ): before {
0 commit comments