feat(settings): empty filename template reverts to recorded original filename (#1071)

Redefine the empty download filename template from a no-op to a bulk
revert: FilenameTemplateUseCase resolves the target from each model's
recorded original_file_name sidecar entry (skipping models without one),
which resolves follow-ups 1 and 2 with a single coherent semantic shared
by the download and bulk-apply paths.

Also replace the browser-native confirm() with a self-managed
confirmation modal (filenameTemplateConfirmModal) that stacks above the
settings modal, since ModalManager would close the settings modal when
opening a registered one.
This commit is contained in:
Will Miao
2026-09-19 10:44:43 +08:00
parent 78d38b449e
commit 474da1b264
17 changed files with 388 additions and 110 deletions
+56 -1
View File
@@ -549,12 +549,17 @@ def _make_filename_template_use_case(
scanner: MockScanner,
lifecycle: StubLifecycleService,
lock_provider: Optional[StubLockProvider] = None,
metadata_loader: Optional[Any] = None,
) -> FilenameTemplateUseCase:
kwargs: Dict[str, Any] = {}
if metadata_loader is not None:
kwargs["metadata_loader"] = metadata_loader
return FilenameTemplateUseCase(
scanner=scanner,
lifecycle_service=lifecycle, # pyright: ignore[reportArgumentType]
lock_provider=lock_provider or StubLockProvider(),
model_type="lora",
**kwargs,
)
@@ -600,7 +605,36 @@ async def test_filename_template_use_case_skips_unchanged_names() -> None:
assert lifecycle.renames == []
async def test_filename_template_use_case_skips_all_when_template_empty() -> None:
async def test_filename_template_use_case_reverts_to_recorded_original_when_template_empty() -> None:
_set_filename_template("")
scanner = MockScanner(cache=MockCache([
_filename_template_model("/library/alpha-renamed.safetensors", "Alpha"),
_filename_template_model("/library/beta.safetensors", "Beta"),
]))
lifecycle = StubLifecycleService()
async def metadata_loader(metadata_path: str) -> Dict[str, Any]:
if metadata_path == "/library/alpha-renamed.metadata.json":
return {"original_file_name": "alpha-original"}
return {}
use_case = _make_filename_template_use_case(
scanner, lifecycle, metadata_loader=metadata_loader
)
result = await use_case.execute(progress_callback=None)
assert result.success_count == 1
assert result.skipped_count == 1
assert lifecycle.renames == [
{
"file_path": "/library/alpha-renamed.safetensors",
"new_file_name": "alpha-original",
}
]
async def test_filename_template_use_case_skips_revert_without_recorded_original() -> None:
_set_filename_template("")
scanner = MockScanner(cache=MockCache([
_filename_template_model("/library/alpha.safetensors", "Alpha"),
@@ -614,6 +648,27 @@ async def test_filename_template_use_case_skips_all_when_template_empty() -> Non
assert lifecycle.renames == []
async def test_filename_template_use_case_skips_revert_matching_current_name() -> None:
_set_filename_template("")
scanner = MockScanner(cache=MockCache([
_filename_template_model("/library/alpha.safetensors", "Alpha"),
]))
lifecycle = StubLifecycleService()
async def metadata_loader(metadata_path: str) -> Dict[str, Any]:
return {"original_file_name": "alpha"}
use_case = _make_filename_template_use_case(
scanner, lifecycle, metadata_loader=metadata_loader
)
result = await use_case.execute(progress_callback=None)
assert result.success_count == 0
assert result.skipped_count == 1
assert lifecycle.renames == []
async def test_filename_template_use_case_counts_conflicts_as_failures() -> None:
_set_filename_template("{model_name}")
scanner = MockScanner(cache=MockCache([