feat: menu contextuel Déplacer/Dupliquer sidebar + réparation drag & drop interne #201 #BUG-117
This commit is contained in:
@@ -1826,9 +1826,13 @@ export const ContextMenuManager = {
|
||||
this._addSeparator();
|
||||
this._addItem('brain', '🧠 BooksLM', () => { import('./bookslm.js').then(m => m.default.open(this._targetVault, this._targetPath)); }, false);
|
||||
this._addSeparator();
|
||||
this._addItem('move', t('common.move'), () => this._moveItem(), isReadonly);
|
||||
this._addItem('copy', t('common.duplicate'), () => this._duplicateItem(), isReadonly);
|
||||
this._addItem('edit', 'Renommer', () => this._renameItem(), isReadonly);
|
||||
this._addItem('trash-2', 'Supprimer', () => this._deleteDirectory(), isReadonly);
|
||||
} else if (type === 'file') {
|
||||
this._addItem('move', t('common.move'), () => this._moveItem(), isReadonly);
|
||||
this._addItem('copy', t('common.duplicate'), () => this._duplicateItem(), isReadonly);
|
||||
this._addItem('edit', 'Renommer', () => this._renameItem(), isReadonly);
|
||||
this._addItem('trash-2', 'Supprimer', () => this._deleteFile(), isReadonly);
|
||||
this._addSeparator();
|
||||
@@ -1918,6 +1922,76 @@ export const ContextMenuManager = {
|
||||
FileOperations.confirmDeleteFile(this._targetVault, this._targetPath);
|
||||
},
|
||||
|
||||
// #BUG-117 — Déplacer / Dupliquer via le modal harmonisé promptTargetFolder
|
||||
// (réutilisé de upload.js, opts pour titre/bouton). Le backend ne supporte
|
||||
// que le même vault ; choix d'un autre vault = toast d'erreur.
|
||||
async _pickDestination(title) {
|
||||
const vault = this._targetVault;
|
||||
const name = this._targetPath.split('/').pop();
|
||||
const parent = this._targetPath.includes('/') ? this._targetPath.slice(0, this._targetPath.lastIndexOf('/')) : '';
|
||||
const { promptTargetFolder } = await import('./upload.js');
|
||||
// Destination pré-sélectionnée = emplacement actuel (sinon '' = racine,
|
||||
// ce qui dupliquait/mouvait à la racine par défaut).
|
||||
const picked = await promptTargetFolder(vault, parent, [], {
|
||||
title: title + ' — ' + name,
|
||||
summaryText: t('ui.move_summary', { name: name }),
|
||||
confirmLabel: title,
|
||||
});
|
||||
if (!picked) return null;
|
||||
if (picked.vault !== vault) {
|
||||
showToast(t('toast.move_cross_vault'), 'error');
|
||||
return null;
|
||||
}
|
||||
return picked;
|
||||
},
|
||||
|
||||
async _moveItem() {
|
||||
const vault = this._targetVault;
|
||||
const path = this._targetPath;
|
||||
const type = this._targetType;
|
||||
let picked = null;
|
||||
try {
|
||||
picked = await this._pickDestination(t('common.move'));
|
||||
if (!picked) return;
|
||||
const result = await api(`/api/move/${encodeURIComponent(vault)}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ source_path: path, destination_dir: picked.targetDir }),
|
||||
});
|
||||
await refreshSidebarTreePreservingState();
|
||||
const nextPath = result.new_path;
|
||||
if (state.currentVault === vault && state.currentPath && (state.currentPath === path || (type === 'directory' && state.currentPath.startsWith(path + '/')))) {
|
||||
if (type === 'file') {
|
||||
await openFile(vault, nextPath);
|
||||
} else {
|
||||
state.currentPath = nextPath + state.currentPath.slice(path.length);
|
||||
syncActiveFileTreeItem();
|
||||
}
|
||||
}
|
||||
showToast(picked.targetDir ? t('toast.moved', { dir: picked.targetDir }) : t('toast.move_root'), 'success');
|
||||
} catch (err) {
|
||||
showToast(err.message || t('common.error'), 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async _duplicateItem() {
|
||||
const vault = this._targetVault;
|
||||
const path = this._targetPath;
|
||||
try {
|
||||
const picked = await this._pickDestination(t('common.duplicate'));
|
||||
if (!picked) return;
|
||||
const result = await api(`/api/duplicate/${encodeURIComponent(vault)}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ source_path: path, destination_dir: picked.targetDir }),
|
||||
});
|
||||
await refreshSidebarTreePreservingState();
|
||||
showToast(t('toast.duplicated', { name: result.new_path.split('/').pop() }), 'success');
|
||||
} catch (err) {
|
||||
showToast(err.message || t('common.error'), 'error');
|
||||
}
|
||||
},
|
||||
|
||||
_copyPath(path) {
|
||||
// Try modern clipboard API first, fall back to execCommand for non-secure contexts
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
|
||||
Reference in New Issue
Block a user