Add drag'n'drop for inner files
This commit is contained in:
parent
205040a599
commit
768d93700e
|
|
@ -1445,56 +1445,142 @@ export class ChatPanel {
|
|||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает событие сброса внешних файлов (Drag-and-Drop) из ОС
|
||||
* Обрабатывает событие сброса внешних файлов (Drag-and-Drop) из ОС
|
||||
* и внутренних файлов из файлового менеджера Obsidian
|
||||
*/
|
||||
private async handleDrop(e: DragEvent): Promise<void> {
|
||||
if (!e.dataTransfer?.files || e.dataTransfer.files.length === 0) {
|
||||
return; // Оброшен не файл (например, просто выделенный текст)
|
||||
if (!e.dataTransfer) {
|
||||
return; // Игнорируем пустые события
|
||||
}
|
||||
|
||||
const isCtrlPressed = e.ctrlKey || e.metaKey;
|
||||
const shouldBePermanent = !isCtrlPressed;
|
||||
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
let addedCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
let absolutePath = '';
|
||||
// 1. Проверяем перетаскивание внешних файлов из файловой системы ОС
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
for (const file of files) {
|
||||
let absolutePath = '';
|
||||
|
||||
// 1. Попытка через скрытое свойство 'path' (иногда оно есть, но не перечисляемо)
|
||||
absolutePath = (file as any).path;
|
||||
|
||||
// 2. Попытка через Electron (если доступен window.require)
|
||||
if (!absolutePath && window.require) {
|
||||
try {
|
||||
const electron = window.require('electron');
|
||||
// 1. Попытка через скрытое свойство 'path' (иногда оно есть, но не перечисляемо)
|
||||
absolutePath = (file as any).path;
|
||||
|
||||
// 2. Попытка через Electron (если доступен window.require)
|
||||
if (!absolutePath && window.require) {
|
||||
try {
|
||||
const electron = window.require('electron');
|
||||
// В некоторых версиях путь можно достать через webUtils из модуля
|
||||
if (electron.webUtils) {
|
||||
absolutePath = electron.webUtils.getPathForFile(file);
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
if (electron.webUtils) {
|
||||
absolutePath = electron.webUtils.getPathForFile(file);
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
// Кэшируем файл (копируем в папку плагина) и получаем метаинформацию
|
||||
const cacheResult = await this.cacheManager.cacheExternalFile(absolutePath);
|
||||
const cacheResult = await this.cacheManager.cacheExternalFile(absolutePath);
|
||||
|
||||
// Формируем Attachment объект
|
||||
const newAttachment: Attachment = {
|
||||
type: 'external',
|
||||
name: cacheResult.name,
|
||||
sourcePath: absolutePath,
|
||||
cachedPath: cacheResult.cachedPath,
|
||||
uuid: cacheResult.uuid,
|
||||
permanent: shouldBePermanent, // D&D файлы прикрепляем разово (как $файл, а не !$)
|
||||
mimeType: cacheResult.mimeType
|
||||
};
|
||||
const newAttachment: Attachment = {
|
||||
type: 'external',
|
||||
name: cacheResult.name,
|
||||
sourcePath: absolutePath,
|
||||
cachedPath: cacheResult.cachedPath,
|
||||
uuid: cacheResult.uuid,
|
||||
permanent: shouldBePermanent,
|
||||
mimeType: cacheResult.mimeType
|
||||
};
|
||||
|
||||
this.currentAttachments.push(newAttachment);
|
||||
addedCount++;
|
||||
} catch (error: any) {
|
||||
console.error(`Ошибка обработки D&D файла ${file.name}:`, error);
|
||||
new Notice(`Не удалось прикрепить ${file.name}. ${error.message || error}`);
|
||||
this.currentAttachments.push(newAttachment);
|
||||
addedCount++;
|
||||
} catch (error: any) {
|
||||
console.error(`Ошибка обработки D&D файла ${file.name}:`, error);
|
||||
new Notice(`Не удалось прикрепить ${file.name}. ${error.message || error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: эту ветку не разбирал, некогда было
|
||||
// 2. Проверяем перетаскивание внутренних файлов Obsidian
|
||||
else {
|
||||
const textData = e.dataTransfer.getData('text/plain');
|
||||
if (textData) {
|
||||
const pathsToProcess: string[] = [];
|
||||
|
||||
// А) Извлекаем пути, если перетянута системная ссылка Obsidian (из Проводника файлов)
|
||||
const lines = textData.split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.startsWith('obsidian://open')) {
|
||||
try {
|
||||
const url = new URL(trimmed);
|
||||
const filePath = url.searchParams.get('file'); // Попутно автоматически декодирует %D0...
|
||||
if (filePath) {
|
||||
pathsToProcess.push(filePath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Не удалось распарсить ссылку Obsidian:", trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Б) Оставляем поддержку, если перетащили текст, содержащий [[вики-ссылки]]
|
||||
const linkRegex = /\[\[(.*?)\]\]/g;
|
||||
let match;
|
||||
while ((match = linkRegex.exec(textData)) !== null) {
|
||||
let linkText = match[1];
|
||||
if (linkText.includes('|')) linkText = linkText.split('|')[0];
|
||||
pathsToProcess.push(linkText);
|
||||
}
|
||||
|
||||
// Убираем дубликаты
|
||||
const uniquePaths = [...new Set(pathsToProcess)];
|
||||
|
||||
for (const targetPath of uniquePaths) {
|
||||
// Ищем файл. Если это был obsidian:// URL, у нас сразу есть точный путь
|
||||
let file = this.props.plugin.app.vault.getAbstractFileByPath(targetPath);
|
||||
|
||||
// Если точный путь не найден (например, это просто имя из вики-ссылки), ищем по кэшу
|
||||
if (!file) {
|
||||
file = this.props.plugin.app.metadataCache.getFirstLinkpathDest(targetPath, '');
|
||||
}
|
||||
|
||||
if (file instanceof TFile) {
|
||||
try {
|
||||
// Отправляем найденный в Vault файл на кеширование
|
||||
const cacheResult = await this.cacheManager.cacheVaultFile(file);
|
||||
|
||||
const promptsFolder = this.props.plugin.settings.promptsFolder;
|
||||
const normalizedPromptsPath = promptsFolder ? promptsFolder.replace(/^\/+|\/+$/g, '') : null;
|
||||
const isPrompt = normalizedPromptsPath && (file.path.startsWith(normalizedPromptsPath + '/') || file.path === normalizedPromptsPath);
|
||||
|
||||
const ext = file.extension.toLowerCase();
|
||||
let mimeType = 'application/octet-stream';
|
||||
if (['md', 'txt', 'csv'].includes(ext)) mimeType = ext === 'md' ? 'text/markdown' : 'text/plain';
|
||||
else if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'].includes(ext)) mimeType = `image/${ext === 'svg' ? 'svg+xml' : ext === 'jpg' ? 'jpeg' : ext}`;
|
||||
else if (ext === 'pdf') mimeType = 'application/pdf';
|
||||
else if (ext === 'json') mimeType = 'application/json';
|
||||
|
||||
// Формируем Attachment объект
|
||||
const newAttachment: Attachment = {
|
||||
type: isPrompt ? 'prompt' : 'file',
|
||||
name: file.name,
|
||||
sourcePath: file.path,
|
||||
cachedPath: cacheResult.cachedPath,
|
||||
uuid: cacheResult.uuid,
|
||||
permanent: shouldBePermanent,
|
||||
mimeType: mimeType
|
||||
};
|
||||
|
||||
this.currentAttachments.push(newAttachment);
|
||||
addedCount++;
|
||||
} catch (error: any) {
|
||||
console.error(`Ошибка обработки внутреннего D&D файла ${file.name}:`, error);
|
||||
new Notice(`Не удалось прикрепить ${file.name}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1504,7 +1590,7 @@ export class ChatPanel {
|
|||
new Notice(`Прикреплено файлов: ${addedCount}`, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------- Scroll Observers ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user