···183 let preventDefault = false
184185 if (clipboardData) {
00000000000000000000000000000000000186 if (clipboardData.types.includes('text/html')) {
187 // Rich-text formatting is pasted, try retrieving plain text
188 const text = clipboardData.getData('text/plain')
···183 let preventDefault = false
184185 if (clipboardData) {
186+ // Check if text is selected and pasted content is a URL
187+ const selection = view.state.selection
188+ const hasSelection = !selection.empty
189+190+ if (hasSelection && clipboardData.types.includes('text/plain')) {
191+ const pastedText = clipboardData.getData('text/plain').trim()
192+ const urlPattern =
193+ /^(?:(?:(?:https?|ftp):)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i
194+195+ if (urlPattern.test(pastedText)) {
196+ const selectedText = view.state.doc.textBetween(
197+ selection.from,
198+ selection.to,
199+ '',
200+ )
201+202+ if (selectedText) {
203+ // Create markdown-style link: [selectedText](url)
204+ const markdownLink = `[${selectedText}](${pastedText})`
205+ const {from, to} = selection
206+207+ view.dispatch(
208+ view.state.tr.replaceWith(
209+ from,
210+ to,
211+ view.state.schema.text(markdownLink),
212+ ),
213+ )
214+215+ preventDefault = true
216+ return true
217+ }
218+ }
219+ }
220+221 if (clipboardData.types.includes('text/html')) {
222 // Rich-text formatting is pasted, try retrieving plain text
223 const text = clipboardData.getData('text/plain')