Skip to content

fix(editor): handle duplicate wallpaper basenames without a sync loop - #134

Merged
bjarneo merged 4 commits into
omacom:mainfrom
itsmunzir:fix/blueprint-wallpaper-collision
Oct 4, 2026
Merged

bjarneo merged 4 commits into
omacom:mainfrom
itsmunzir:fix/blueprint-wallpaper-collision

Conversation

@itsmunzir

Copy link
Copy Markdown
Contributor

Refs #130.

Problem

Two related failures around wallpapers.

  1. Collision on staging. The backend copies every background into a single
    flat backgrounds/ directory, so a blueprint that lists the same wallpaper
    twice, or two wallpapers that share a filename (e.g. ~/a/wall.png and
    ~/b/wall.png), is rejected with a basename collision. The editor happily
    accepted such a list and only failed when the user applied the theme.
  2. Repeated state echo. A backend-pushed ipc-state-changed snapshot was
    mirrored into the stores unconditionally. Re-writing equal arrays/objects
    re-ran App's SyncState $effect, which pushed the same snapshot straight
    back to Go and produced another echo.

Solution

Deduplicate where the set can still be acted on, and make the backend mirror
idempotent.

  • dedupeAdditionalImages(images, primaryWallpaper) drops entries by full
    path and by basename, first entry wins; the primary wallpaper is seeded into
    the seen sets so an additional image cannot shadow it.
  • loadBlueprintIntoEditor runs the filter and shows Skipped N duplicate wallpaper(s) instead of the generic load toast when entries were dropped.
  • addAdditionalImage returns whether the file was accepted;
    AdditionalImages.svelte toasts Skipped — another wallpaper already uses that filename rather than reporting a successful add.
  • setWallpaperPath, restoreHistorySnapshot, setAdditionalImages and
    swapMainWithAdditional reuse the filter, so undo and swap cannot restore a
    colliding pair.
  • applyBackendState compares each pushed value against local state
    (sameSerialized for maps, sameStringList for slices) and skips no-op
    writes; App.svelte keeps a lastSyncedSignature and only dispatches
    syncStateToBackend when the snapshot actually changed.

Verification

Observed while preparing this change:

  • vitest run in frontend/: 122 tests pass, including the new
    frontend/tests/duplicate-wallpapers.test.ts suite (blueprint load with
    duplicate paths and shared basenames, add/swap/undo, and the backend echo
    round-trip).
  • svelte-check: 0 errors, 0 warnings.
  • Browser smoke against the Vite dev server: importing a blueprint with a
    duplicated wallpaper keeps exactly one image and shows the skip warning.
  • Native Wails backend integration was not run here; the collision itself
    is evidenced by the backend's flat staging directory rather than by a
    reproduced end-to-end run.

Limitations

The reported freeze (#130) is addressed from the state-echo path in the
frontend source; the reporter's exact freeze was not reproduced in a native
Wails build, so this is a source-level fix for the loop plus a concrete fix
for the collision. Reporter confirmation on a native build would be
appreciated.

The backend stages every background into one flat `backgrounds/` directory,
so a blueprint that lists the same wallpaper twice — or two wallpapers that
share a basename — is rejected with a basename collision and the whole apply
fails. Loading, adding, restoring and swapping wallpapers now drop entries
that cannot be staged (first entry wins, primary wallpaper counted):

- `dedupeAdditionalImages` filters by full path and by basename;
  `loadBlueprintIntoEditor` reports the dropped count with an explicit
  toast, and `AdditionalImages.svelte` reports a skipped file instead of
  silently adding it.
- `setWallpaperPath`, `restoreHistorySnapshot`, `setAdditionalImages` and
  `swapMainWithAdditional` apply the same filter, so undo or swap cannot
  reintroduce a collision.

The same entries echoed back from the backend re-armed App's SyncState
`$effect` with an unchanged snapshot, which pushed the identical state back
to Go and re-triggered the echo. `applyBackendState` now skips values that
already match local state, and the sync effect only dispatches a snapshot
whose signature changed since the last dispatch.

Refs omacom#130
@itsmunzir

Copy link
Copy Markdown
Contributor Author

Gentle check-in — nothing here needs a decision today. The branch is up to date with main and ready whenever you have a look; happy to change anything you want first.

The real freeze in omacom#130 came from the keyed each block in
AdditionalImages.svelte, which throws on a repeated path. Remove the key,
and correct the comments that blamed a state echo loop.

Go now stages an exact repeat of a path once, so the CLI and IPC apply
paths accept blueprints with duplicate entries. Two different files with
the same filename still fail with the collision error.

Show a toast when a new main wallpaper or an undo drops an additional
image, because undo cannot restore it. Reuse dedupeAdditionalImages in
addAdditionalImage.
# Conflicts:
#	frontend/src/lib/components/editor/AdditionalImages.svelte
The favorites, local, Wallhaven and GitHub browsers showed "Added" even
when addAdditionalImage rejected a file with a duplicate filename.
@bjarneo
bjarneo merged commit 2a5f08e into omacom:main Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants