fix(dragdrop): amélioration sélection répertoire cible, déplacement arborescence et ouverture auto IA (#89)
CI / lint (push) Successful in 1m17s
CI / security (push) Successful in 51s
CI / test (push) Successful in 3m11s
CI / build (push) Successful in 48s
CI / e2e (push) Successful in 11m2s

This commit is contained in:
2026-09-14 15:55:41 -04:00
parent 1db3e0ad2f
commit ba7b49c35b
5 changed files with 144 additions and 22 deletions
+30 -15
View File
@@ -13,6 +13,7 @@ import { t } from './i18n.js';
import { showToast } from './ui.js';
import { refreshSidebarTreePreservingState } from './sidebar.js';
import { readDataTransferEntries, handleVaultDropUpload } from './upload.js';
import booksLM from './bookslm.js';
// ---------------------------------------------------------------------------
// State
@@ -52,15 +53,14 @@ function getDropTarget(el) {
// Directory tree item
const dirItem = closest('.tree-item[data-path]', el);
if (dirItem) {
// Only directories are valid targets
const icon = dirItem.querySelector('[data-lucide]');
const isDir = icon && (icon.getAttribute('data-lucide') === 'folder'
|| icon.getAttribute('data-lucide') === 'chevron-right'
|| icon.getAttribute('data-lucide') === 'chevron-down');
// Heuristic: if the tree item has a child container sibling, it's a directory
const isExplicitDir = dirItem.dataset.type === 'directory';
const icon = dirItem.querySelector('[data-lucide], i');
const iconName = icon?.getAttribute('data-lucide');
const isFolderIcon = iconName === 'folder' || iconName === 'chevron-right' || iconName === 'chevron-down';
const hasChildren = dirItem.nextElementSibling
&& dirItem.nextElementSibling.classList.contains('tree-children');
if (isDir || hasChildren) {
if (isExplicitDir || isFolderIcon || hasChildren) {
return {
vault: dirItem.dataset.vault,
targetDir: dirItem.dataset.path,
@@ -149,9 +149,10 @@ function onDragStart(e) {
if (!vault || path === undefined) return;
// Determine type: directory or file
const isExplicitDir = treeItem.dataset.type === 'directory';
const hasChildren = treeItem.nextElementSibling
&& treeItem.nextElementSibling.classList.contains('tree-children');
const type = hasChildren ? 'directory' : 'file';
const type = (isExplicitDir || hasChildren) ? 'directory' : 'file';
dragState = { vault, path, type, element: treeItem };
@@ -159,6 +160,9 @@ function onDragStart(e) {
e.dataTransfer.effectAllowed = 'move';
// Prefix with 'tree:' so pane drop zones can distinguish from tab drags
e.dataTransfer.setData('text/plain', 'tree:' + JSON.stringify({ vault, path }));
e.dataTransfer.setData('tree:vault', vault);
e.dataTransfer.setData('tree:path', path);
e.dataTransfer.setData('tree:type', type);
// Visual: reduce opacity of source
treeItem.classList.add(DRAG_SOURCE_CLASS);
@@ -170,10 +174,12 @@ function onDragStart(e) {
if (item.classList.contains('vault-item')) {
item.classList.add(DRAG_VALID_TARGET_CLASS);
} else {
const icon = item.querySelector('[data-lucide]');
const icon = item.querySelector('[data-lucide], i');
const iconName = icon?.getAttribute('data-lucide');
const isFolder = item.dataset.type === 'directory' || iconName === 'folder' || iconName === 'chevron-right' || iconName === 'chevron-down';
const hasChildContainer = item.nextElementSibling
&& item.nextElementSibling.classList.contains('tree-children');
if (icon && (icon.getAttribute('data-lucide') === 'folder' || hasChildContainer)) {
if (isFolder || hasChildContainer) {
item.classList.add(DRAG_VALID_TARGET_CLASS);
}
}
@@ -436,16 +442,25 @@ export function initGlobalDropOverlay() {
if (zoneType === 'vault') {
const activeVault = window.state?.activeVault || (document.querySelector('.tree-item.vault-item')?.dataset?.vault);
if (activeVault) {
await handleVaultDropUpload(activeVault, '', items);
// Pass null for targetDir so user is prompted to choose destination folder
await handleVaultDropUpload(activeVault, null, items);
} else {
showToast('Aucun vault sélectionné', 'error');
}
} else if (zoneType === 'ai') {
// Dispatch or pass to BooksLM assistant
if (window.BooksLMInstance) {
window.BooksLMInstance.handleExternalDroppedItems(items);
// Automatically open assistant if not already open and pass items
if (booksLM) {
if (!booksLM._isOpen) {
await booksLM.openForCurrentContext();
}
await booksLM.handleExternalDroppedItems(items);
} else if (window.BooksLMInstance) {
if (!window.BooksLMInstance._isOpen) {
await window.BooksLMInstance.openForCurrentContext();
}
await window.BooksLMInstance.handleExternalDroppedItems(items);
} else {
showToast("Assistant IA non initialisé", 'warning');
showToast("Assistant IA non disponible", 'warning');
}
}
});