feat(recipes): add reconnect remediation paths for missing recipe LoRAs

- Snapshot pre-rematch entry state (reconnectSnapshot) so rematched
  entries can be undone via the existing restore flow
- Bulk missing-LoRA downloads mark unresolvable failures hash-invalid,
  flipping those entries from download to reconnect candidacy
- Recipe modal always offers a reconnect action next to download for
  missing LoRA entries
- Rematch runs collect an opt-in relaxed-matching choice (also reconnect
  missing models by file name) via a pre-run options dialog on the
  global, bulk and single-recipe entries
- L4 (filename-level) matches are listed in a results dialog with
  per-entry undo
This commit is contained in:
Will Miao
2026-09-09 06:59:54 +08:00
parent e747946f7a
commit 1b5cbbbaa0
33 changed files with 2103 additions and 76 deletions
+15
View File
@@ -309,6 +309,21 @@ describe('RecipeSidebarApiClient bulk operations', () => {
expect(global.fetch).not.toHaveBeenCalled();
});
it('includes relaxed in the bulk rematch body only when opted in', async () => {
const api = new RecipeSidebarApiClient();
global.fetch.mockResolvedValue({
ok: true,
json: async () => ({ success: true, total: 1, rematched: 1, skipped: 0, errors: 0, recipes: [] }),
});
await api.rematchBulkModels(['/recipes/a.webp'], { relaxed: true });
expect(JSON.parse(global.fetch.mock.calls[0][1].body)).toEqual({
recipe_ids: ['a'],
relaxed: true,
});
});
it('throws the backend error when bulk rematch fails', async () => {
const api = new RecipeSidebarApiClient();
global.fetch.mockResolvedValue({