feat(sidebar): rename folders from the sidebar (#999)

Follows the folder create/delete work: a typo'd directory could be
removed but not corrected, and for a folder holding models the only fix
was to move every model out by hand.

Adds POST /api/lm/{prefix}/rename-folder. Unlike the delete path this one
deliberately works on folders that hold models — a rename keeps every
file, so nothing is cascaded over: the directory is renamed on disk and
the scanner re-keys the records that pointed at the old prefix (recorded
folder list, cache file_path/folder/preview_url, hash and autov3 index
paths, excluded-model paths, and the metadata sidecars that travelled
with the directory). Ancestors are never touched, and only the leaf name
is accepted so a rename can never escape its parent.

Library roots, top-level symlinks and folders holding a staged delete are
refused; the last because a staging manifest records absolute
original/staged paths, so moving it would break undo and purge. A name
collision is a 409 target_exists conflict.

The sidebar reuses the inline-row idiom from folder creation: prefilled
with the current name, inserted in place of the node with that node
hidden while editing, Enter confirms and Escape/blur cancels. The
persisted selection and the expanded set are re-keyed across the rename
so the user keeps their place in the refreshed tree.
This commit is contained in:
Will Miao
2026-09-15 20:10:36 +08:00
parent 4938faa049
commit 9bbe57ee85
22 changed files with 1258 additions and 0 deletions
+9
View File
@@ -1283,6 +1283,15 @@
"unsupported": "[TODO: Translate] Folder deletion is not supported on this page",
"noRoot": "[TODO: Translate] No model root is configured"
},
"renameFolder": "[TODO: Translate] Rename folder",
"renameFolderResult": {
"success": "[TODO: Translate] Folder renamed to \"{name}\"",
"failed": "[TODO: Translate] Failed to rename folder: {message}",
"targetExists": "[TODO: Translate] A folder with that name already exists here",
"busy": "[TODO: Translate] A deletion is still pending inside this folder. Wait for the undo window to expire.",
"unsupported": "[TODO: Translate] Folder renaming is not supported on this page",
"noRoot": "[TODO: Translate] No model root is configured"
},
"dragDrop": {
"unableToResolveRoot": "Zielpfad für das Verschieben konnte nicht ermittelt werden.",
"moveUnsupported": "Verschieben wird für dieses Element nicht unterstützt.",