1
1
import {htmlEscape} from 'escape-goat';
2
2
import {POST} from '../../modules/fetch.js';
3
3
import {imageInfo} from '../../utils/image.js';
4
+ import {getPastedContent, replaceTextareaSelection} from '../../utils/dom.js';
5
+ import {isUrl} from '../../utils/url.js';
4
6
5
7
async function uploadFile(file, uploadUrl) {
6
8
const formData = new FormData();
@@ -10,17 +12,6 @@ async function uploadFile(file, uploadUrl) {
10
12
return await res.json();
11
13
}
12
14
13
- function clipboardPastedImages(e) {
14
- if (!e.clipboardData) return [];
15
-
16
- const files = [];
17
- for (const item of e.clipboardData.items || []) {
18
- if (!item.type || !item.type.startsWith('image/')) continue;
19
- files.push(item.getAsFile());
20
- }
21
- return files;
22
- }
23
-
24
15
function triggerEditorContentChanged(target) {
25
16
target.dispatchEvent(new CustomEvent('ce-editor-content-changed', {bubbles: true}));
26
17
}
@@ -91,20 +82,16 @@ class CodeMirrorEditor {
91
82
}
92
83
}
93
84
94
- const uploadClipboardImage = async (editor, dropzone, e) => {
85
+ async function handleClipboardImages (editor, dropzone, images, e) {
95
86
const uploadUrl = dropzone.getAttribute('data-upload-url');
96
87
const filesContainer = dropzone.querySelector('.files');
97
88
98
- if (!uploadUrl || !filesContainer) return;
89
+ if (!dropzone || ! uploadUrl || !filesContainer || !images.length ) return;
99
90
100
- const pastedImages = clipboardPastedImages(e);
101
- if (!pastedImages || pastedImages.length === 0) {
102
- return;
103
- }
104
91
e.preventDefault();
105
92
e.stopPropagation();
106
93
107
- for (const img of pastedImages ) {
94
+ for (const img of images ) {
108
95
const name = img.name.slice(0, img.name.lastIndexOf('.'));
109
96
110
97
const placeholder = ``;
@@ -131,18 +118,38 @@ const uploadClipboardImage = async (editor, dropzone, e) => {
131
118
input.value = uuid;
132
119
filesContainer.append(input);
133
120
}
134
- };
121
+ }
122
+
123
+ function handleClipboardText(textarea, text, e) {
124
+ // when pasting links over selected text, turn it into [text](link), except when shift key is held
125
+ const {value, selectionStart, selectionEnd, _giteaShiftDown} = textarea;
126
+ if (_giteaShiftDown) return;
127
+ const selectedText = value.substring(selectionStart, selectionEnd);
128
+ const trimmedText = text.trim();
129
+ if (selectedText && isUrl(trimmedText)) {
130
+ e.stopPropagation();
131
+ e.preventDefault();
132
+ replaceTextareaSelection(textarea, `[${selectedText}](${trimmedText})`);
133
+ }
134
+ }
135
135
136
136
export function initEasyMDEImagePaste(easyMDE, dropzone) {
137
- if (!dropzone) return;
138
- easyMDE.codemirror.on('paste', async (_, e) => {
139
- return uploadClipboardImage(new CodeMirrorEditor(easyMDE.codemirror), dropzone, e);
137
+ easyMDE.codemirror.on('paste', (_, e) => {
138
+ const {images} = getPastedContent(e);
139
+ if (images.length) {
140
+ handleClipboardImages(new CodeMirrorEditor(easyMDE.codemirror), dropzone, images, e);
141
+ }
140
142
});
141
143
}
142
144
143
145
export function initTextareaImagePaste(textarea, dropzone) {
144
- if (!dropzone) return;
145
- textarea.addEventListener('paste', async (e) => {
146
- return uploadClipboardImage(new TextareaEditor(textarea), dropzone, e);
146
+ textarea.addEventListener('paste', (e) => {
147
+ const {images, text} = getPastedContent(e);
148
+ if (text) {
149
+ handleClipboardText(textarea, text, e);
150
+ }
151
+ if (images.length) {
152
+ handleClipboardImages(new TextareaEditor(textarea), dropzone, images, e);
153
+ }
147
154
});
148
155
}
0 commit comments