클립보드 이미지 붙여넣기 업로드를 지원한다.

Ctrl/Cmd+V로 이미지를 선택해 기존 업로드 흐름으로 바로 올릴 수 있게 한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-16 11:28:03 +09:00
parent 251d70642e
commit 1a54f0304b
2 changed files with 77 additions and 1 deletions

View File

@@ -58,7 +58,7 @@ dreamgirl_require_login_page();
<div id="upload-panel" class="upload-panel"> <div id="upload-panel" class="upload-panel">
<div class="upload-title">이미지 추가</div> <div class="upload-title">이미지 추가</div>
<div id="dropzone" class="dropzone" tabindex="0"> <div id="dropzone" class="dropzone" tabindex="0">
<div class="dropzone-text">드래그&드랍 또는 파일 선택</div> <div class="dropzone-text">드래그&드랍, 붙여넣기(Ctrl/Cmd+V) 또는 파일 선택</div>
<input id="file-input" class="file-input" type="file" accept="image/*" /> <input id="file-input" class="file-input" type="file" accept="image/*" />
</div> </div>
<div id="upload-preview" class="upload-preview" style="display:none;"> <div id="upload-preview" class="upload-preview" style="display:none;">

View File

@@ -61,6 +61,80 @@
showPreview(f); showPreview(f);
} }
function pad2(n) {
return (n < 10 ? '0' : '') + n;
}
function pasteFilename(mime) {
var now = new Date();
var stamp =
now.getFullYear() +
pad2(now.getMonth() + 1) +
pad2(now.getDate()) +
'_' +
pad2(now.getHours()) +
pad2(now.getMinutes()) +
pad2(now.getSeconds());
var ext = 'png';
if (mime === 'image/jpeg') ext = 'jpg';
else if (mime === 'image/gif') ext = 'gif';
else if (mime === 'image/webp') ext = 'webp';
return 'paste_' + stamp + '.' + ext;
}
function fileFromClipboardItem(item) {
if (!item || typeof item.getAsFile !== 'function') return null;
var blob = item.getAsFile();
if (!blob || !isImageFile(blob)) return null;
var name = blob.name && blob.name !== 'image.png'
? blob.name
: pasteFilename(blob.type || 'image/png');
try {
return new File([blob], name, { type: blob.type || 'image/png' });
} catch (e) {
// Older browsers may not support File constructor
blob.name = name;
return blob;
}
}
function onPaste(e) {
var target = e.target;
if (target) {
var tag = (target.tagName || '').toLowerCase();
if (tag === 'input' || tag === 'textarea' || target.isContentEditable) {
return;
}
}
var clipboard = e.clipboardData || window.clipboardData;
if (!clipboard) return;
var items = clipboard.items;
if (items && items.length) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item || !item.type || item.type.indexOf('image/') !== 0) continue;
var file = fileFromClipboardItem(item);
if (!file) continue;
e.preventDefault();
showPreview(file);
setStatus('클립보드 이미지 선택됨. 업로드를 눌러주세요.');
return;
}
}
// Fallback: some browsers expose files directly
if (clipboard.files && clipboard.files.length) {
var pasted = clipboard.files[0];
if (isImageFile(pasted)) {
e.preventDefault();
onFiles(clipboard.files);
setStatus('클립보드 이미지 선택됨. 업로드를 눌러주세요.');
}
}
}
dropzone.addEventListener('dragover', function (e) { dropzone.addEventListener('dragover', function (e) {
e.preventDefault(); e.preventDefault();
dropzone.classList.add('dragover'); dropzone.classList.add('dragover');
@@ -78,6 +152,8 @@
onFiles(fileInput.files); onFiles(fileInput.files);
}); });
document.addEventListener('paste', onPaste);
uploadBtn.addEventListener('click', function () { uploadBtn.addEventListener('click', function () {
if (!selectedFile) return; if (!selectedFile) return;
uploadBtn.disabled = true; uploadBtn.disabled = true;