feat: add paste and drag-and-drop support for chat files
- Added paste event handler to detect and handle files from clipboard - Support for pasting images and files with Ctrl+V / Cmd+V - Added drag-and-drop file upload functionality - Visual feedback with green highlight during drag-over - Supports multiple file paste/drop at once - Prevents text insertion when files are pasted - Uses clipboard items API for better file detection - Fallback to clipboard files for broader compatibility - Console logging for debugging paste/drop operations
This commit is contained in:
parent
54a3e46b3d
commit
1e6f60ddfe
@ -172,6 +172,14 @@ main.main {
|
||||
box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb), 0.15);
|
||||
}
|
||||
|
||||
/* Стиль при перетаскивании файлов */
|
||||
.composer-textarea.drag-over {
|
||||
border-color: var(--bs-success);
|
||||
background: rgba(var(--bs-success-rgb), 0.05);
|
||||
box-shadow: 0 0 0 0.2rem rgba(var(--bs-success-rgb), 0.15);
|
||||
}
|
||||
|
||||
|
||||
.btn-attachment {
|
||||
padding: 8px 12px;
|
||||
border-radius: 50%;
|
||||
|
||||
@ -100,6 +100,31 @@ class ChatComposer {
|
||||
this.textarea.addEventListener('input', () => {
|
||||
this.handleTyping();
|
||||
});
|
||||
|
||||
// Обработка вставки из буфера обмена
|
||||
this.textarea.addEventListener('paste', (e) => {
|
||||
this.handlePaste(e);
|
||||
});
|
||||
|
||||
// Обработка drag-and-drop
|
||||
this.textarea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.textarea.classList.add('drag-over');
|
||||
});
|
||||
|
||||
this.textarea.addEventListener('dragleave', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.textarea.classList.remove('drag-over');
|
||||
});
|
||||
|
||||
this.textarea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.textarea.classList.remove('drag-over');
|
||||
this.handleDrop(e);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('chat:message-received', this.boundMessageHandler);
|
||||
@ -155,6 +180,92 @@ class ChatComposer {
|
||||
this.renderPreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка вставки из буфера обмена
|
||||
* Поддерживает вставку изображений и файлов
|
||||
*/
|
||||
handlePaste(event) {
|
||||
const clipboardData = event.clipboardData || window.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const items = Array.from(clipboardData.items || []);
|
||||
const files = Array.from(clipboardData.files || []);
|
||||
|
||||
// Проверяем наличие файлов
|
||||
let hasFiles = false;
|
||||
const pastedFiles = [];
|
||||
|
||||
// Сначала проверяем items (более надежный способ)
|
||||
items.forEach((item) => {
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
pastedFiles.push(file);
|
||||
hasFiles = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Если не нашли через items, пробуем files
|
||||
if (!hasFiles && files.length > 0) {
|
||||
pastedFiles.push(...files);
|
||||
hasFiles = true;
|
||||
}
|
||||
|
||||
// Если есть файлы - обрабатываем их
|
||||
if (hasFiles && pastedFiles.length > 0) {
|
||||
console.log('[ChatComposer] Pasted files:', pastedFiles);
|
||||
|
||||
// Предотвращаем вставку текста/HTML если вставлены файлы
|
||||
event.preventDefault();
|
||||
|
||||
// Добавляем файлы в selectedFiles
|
||||
pastedFiles.forEach((file) => {
|
||||
const uuidSupported = window.crypto && typeof window.crypto.randomUUID === 'function';
|
||||
const fileId = uuidSupported ? window.crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
|
||||
this.selectedFiles.push({ id: fileId, file });
|
||||
});
|
||||
|
||||
// Обновляем превью
|
||||
this.renderPreview();
|
||||
|
||||
// Показываем уведомление пользователю
|
||||
const fileCount = pastedFiles.length;
|
||||
const fileWord = fileCount === 1 ? 'файл' : fileCount < 5 ? 'файла' : 'файлов';
|
||||
console.log(`[ChatComposer] Добавлено ${fileCount} ${fileWord} из буфера обмена`);
|
||||
}
|
||||
// Если файлов нет - оставляем стандартное поведение (вставка текста)
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка перетаскивания файлов (drag-and-drop)
|
||||
*/
|
||||
handleDrop(event) {
|
||||
const dataTransfer = event.dataTransfer;
|
||||
if (!dataTransfer) return;
|
||||
|
||||
const files = Array.from(dataTransfer.files || []);
|
||||
|
||||
if (files.length > 0) {
|
||||
console.log('[ChatComposer] Dropped files:', files);
|
||||
|
||||
// Добавляем файлы в selectedFiles
|
||||
files.forEach((file) => {
|
||||
const uuidSupported = window.crypto && typeof window.crypto.randomUUID === 'function';
|
||||
const fileId = uuidSupported ? window.crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
|
||||
this.selectedFiles.push({ id: fileId, file });
|
||||
});
|
||||
|
||||
// Обновляем превью
|
||||
this.renderPreview();
|
||||
|
||||
// Показываем уведомление пользователю
|
||||
const fileCount = files.length;
|
||||
const fileWord = fileCount === 1 ? 'файл' : fileCount < 5 ? 'файла' : 'файлов';
|
||||
console.log(`[ChatComposer] Добавлено ${fileCount} ${fileWord} перетаскиванием`);
|
||||
}
|
||||
}
|
||||
|
||||
handleTyping() {
|
||||
const now = Date.now();
|
||||
if (now - this.lastTypingSent > this.typingThrottle) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user