feat(icons): preserve user-uploaded icons from bulk fetch/clear

Auto-fetched favicons and user uploads shared one icon_b64 column, so
"Clear cache" wiped custom uploads and "Re-fetch all" overwrote them — data
loss the user can't undo (a custom icon exists precisely because DDG can't
fetch that site).

New icon_custom flag (0=fetched cache, 1=user upload). Invariant: an
automatic action never touches a custom icon; only the user changes/removes
it per entry.

- Schema: icon_custom INTEGER on vault_entries.
- POST /entries/{id}/icon takes {custom}; GET /entries echoes icon_custom;
  DELETE /entries/icons/all filters COALESCE(icon_custom,0)=0.
- saveEntryIcon(id, uri, isCustom): upload→true, fetch/remove→false.
- backfillFavicons skips icon_custom even on force (Re-fetch all).
- clearAllFavicons clears only non-custom locally + refreshes the open editor
  (the icon preview didn't update on clear before).
- duplicate re-stamps a copied custom icon; import-overwrite carries the flag.

Not propagated across bulk-import / sync (the bulk INSERT doesn't read it) —
a restored icon counts as fetched. Documented; custom icons are typically
self-hosted sites DDG won't re-fetch anyway.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-25 15:11:25 +01:00
parent f45b96d147
commit 18fe13a861
6 changed files with 67 additions and 11 deletions
+1 -1
View File
@@ -716,7 +716,7 @@ async function doImport() {
// PUT ignores icon_b64 (dedicated endpoint owns it), so
// restore the file's icon separately — else overwriting
// an entry whose icon was cleared never brings it back.
if (src.icon_b64) await saveEntryIcon(local.id, src.icon_b64);
if (src.icon_b64) await saveEntryIcon(local.id, src.icon_b64, !!src.icon_custom);
overwritten++;
} catch (_) { /* skip the single row on failure */ }
}