mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-08 10:32:13 -03:00
fix(sidebar): keep the recipes storage dir out of folder management
The default recipes dir (<first lora root>/recipes) is auto-created by the recipe scanner and recorded by the model-root walks as an empty folder, so it showed up in the folder sidebar — and since recipe files are not model weight files, the folder delete endpoint's model_count check let a right-click delete wipe the whole recipe library via shutil.rmtree. - scan walks (full scan, reconcile, all-folders backfill) no longer descend into the effective recipes dir, and get_all_folders() filters it out of snapshots persisted before the exclusion existed - delete_folder/rename_folder refuse the recipes dir and any ancestor of it with code "protected" - the delete modal renders a protected state (confirm hidden, per-row checkbox disabled) instead of a deletable 'no models' prediction - RecipeScanner.recipes_dir now shares the side-effect-free resolver in py/utils/recipes_paths.py
This commit is contained in:
@@ -1301,6 +1301,8 @@ export class SidebarManager {
|
||||
...this._notEmptyBlocker(error?.manifest),
|
||||
...options,
|
||||
});
|
||||
} else if (error?.code === 'protected') {
|
||||
this._renderDeleteFolderModal(relativePath, 'protected', options);
|
||||
} else if (error?.code === 'busy') {
|
||||
this._renderDeleteFolderModal(relativePath, 'busy', options);
|
||||
} else if (error?.code === 'missing') {
|
||||
@@ -1458,6 +1460,9 @@ export class SidebarManager {
|
||||
entry.modelCount = Number(error?.manifest?.model_count) || 0;
|
||||
entry.excludedCount = Number(error?.manifest?.excluded_model_count) || 0;
|
||||
entry.checked = false;
|
||||
} else if (error?.code === 'protected') {
|
||||
entry.status = 'protected';
|
||||
entry.checked = false;
|
||||
} else if (error?.code === 'busy') {
|
||||
entry.status = 'busy';
|
||||
entry.checked = false;
|
||||
@@ -1484,7 +1489,10 @@ export class SidebarManager {
|
||||
nodes.checkbox.disabled = !selectable;
|
||||
nodes.checkbox.checked = entry.checked;
|
||||
nodes.status.textContent = this._deleteFolderRowStatus(entry);
|
||||
nodes.status.classList.toggle('blocked', entry.status === 'not_empty');
|
||||
nodes.status.classList.toggle(
|
||||
'blocked',
|
||||
entry.status === 'not_empty' || entry.status === 'protected'
|
||||
);
|
||||
}
|
||||
|
||||
/** Whether a probed copy may be selected for deletion. */
|
||||
@@ -1512,6 +1520,11 @@ export class SidebarManager {
|
||||
return translate('sidebar.folderRoot.busyStatus', {}, 'a deletion is still pending');
|
||||
case 'missing':
|
||||
return translate('sidebar.folderRoot.missingStatus', {}, 'no longer exists on disk');
|
||||
case 'protected':
|
||||
return translate(
|
||||
'sidebar.folderRoot.protectedStatus', {},
|
||||
'holds the recipe library — cannot be deleted here'
|
||||
);
|
||||
case 'symlink':
|
||||
return translate(
|
||||
'sidebar.folderRoot.symlinkStatus', {},
|
||||
@@ -1738,9 +1751,10 @@ export class SidebarManager {
|
||||
* `state` is 'confirm' (deletion may proceed), 'blocked' (models would be
|
||||
* cascaded over, which the backend refuses), 'busy' (a staged delete is
|
||||
* still pending inside the folder), 'missing' (no root holds the node's
|
||||
* directory any more) or 'symlink' (every copy is a symbolic link, which the
|
||||
* backend refuses to remove). `checking` keeps the confirm button disabled
|
||||
* while the authoritative server-side check runs.
|
||||
* directory any more), 'symlink' (every copy is a symbolic link, which the
|
||||
* backend refuses to remove) or 'protected' (the folder holds the recipe
|
||||
* library, which the backend refuses to remove). `checking` keeps the
|
||||
* confirm button disabled while the authoritative server-side check runs.
|
||||
*
|
||||
* This is the single-target form, used when the node maps to at most one
|
||||
* directory; an ambiguous node renders the multi-root list instead (see
|
||||
@@ -1816,6 +1830,14 @@ export class SidebarManager {
|
||||
'sidebar.deleteFolderModal.symlinkMessage', {},
|
||||
'Every copy of this folder is a symbolic link; remove the link or its target outside LoRA Manager.'
|
||||
);
|
||||
} else if (state === 'protected') {
|
||||
title.textContent = translate(
|
||||
'sidebar.deleteFolderModal.protectedTitle', {}, 'Folder is protected'
|
||||
);
|
||||
message.textContent = translate(
|
||||
'sidebar.deleteFolderModal.protectedMessage', {},
|
||||
'This folder holds the recipe library and cannot be deleted from LoRA Manager.'
|
||||
);
|
||||
} else {
|
||||
title.textContent = translate(
|
||||
'sidebar.deleteFolderModal.notEmptyTitle', {}, 'Folder is not empty'
|
||||
|
||||
Reference in New Issue
Block a user