Skip to content

feat(ui): add canvas snapshot save/restore functionality#8978

Open
GGSSKK wants to merge 3 commits intoinvoke-ai:mainfrom
GGSSKK:feat/canvas-snapshot-save-restore
Open

feat(ui): add canvas snapshot save/restore functionality#8978
GGSSKK wants to merge 3 commits intoinvoke-ai:mainfrom
GGSSKK:feat/canvas-snapshot-save-restore

Conversation

@GGSSKK
Copy link
Copy Markdown
Contributor

@GGSSKK GGSSKK commented Mar 20, 2026

Summary

Adds a canvas snapshot save/restore button to the toolbar (camera icon). Users can save the
current canvas state at any time and restore it later — useful when the canvas resets unexpectedly
due to freezes or reloads.

Reuses the existing client_state API with a canvas_snapshot: key prefix, keeping changes
minimal.

snapshot.mp4

Related Issues / Discussions

Related to #6554 (canvas state not persisting). This provides a manual workaround by letting users
explicitly save/restore canvas state.

QA Instructions

  1. Add layers and adjust bbox on the canvas
  2. Click the camera icon in the toolbar → save a snapshot (leave name blank for auto date/time)
  3. Reset canvas via New Session
  4. Click the camera icon → select the snapshot to restore
  5. Verify layers, masks, and bbox are restored
  6. Test deleting a snapshot via the trash icon

Merge Plan

Simple merge. No DB schema changes — uses existing client_state table.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • Changes to a redux slice have a corresponding migration — N/A, no state shape change (new
    reducer only)
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

Add ability to save and restore canvas state snapshots, allowing users
to preserve their canvas layout at any point and restore it later.
This is useful when the canvas freezes or resets unexpectedly.

Backend:
- Add get_keys_by_prefix and delete_by_key to client_state persistence
- Add corresponding API endpoints

Frontend:
- Add canvasSnapshotRestored reducer to canvasSlice
- Add useCanvasSnapshots hook for snapshot CRUD operations
- Add CanvasToolbarSnapshotMenuButton with save/restore UI
- Add i18n keys for snapshot feature
- Regenerate API schema types

Tests:
- Add tests for new client_state endpoints (prefix search, key deletion)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added api python PRs that change python files services PRs that change app services frontend PRs that change frontend files python-tests PRs that change python tests labels Mar 20, 2026
@DustyShoe
Copy link
Copy Markdown
Collaborator

A very welcome feature. Also useful if you want temporarily switch to something else.
Should have mentioned that it breaks if user did clear intermediates in settings.

@DustyShoe
Copy link
Copy Markdown
Collaborator

Here's some finding done with LLM regarding this PR as a heads up for reviewer.

Review Pass 1: Broader Review

  • High: Restoring a canvas snapshot copies the entire saved bbox, including modelBase. That can desynchronize the canvas from the currently selected model if the user changed models after saving the snapshot. As a result, size, grid, and optimal-dimension logic may run against the wrong model base.
  • Medium: Snapshot restore currently goes through the normal undoable canvas history. That means Undo right after restore can jump back to the pre-restore state, and history also keeps another full canvas state in memory. This feels inconsistent with other full-canvas replacement flows that clear history.
  • Low: Snapshot ordering is currently based on rowid DESC, not on last update time. If a user overwrites an existing snapshot with the same name, it will not move to the top of the list even though it was just re-saved.
  • Optimization: The snapshot client code uses manual fetch calls and manual auth header handling even though the frontend already has a shared API transport layer. Moving this into the existing API layer would make it easier to maintain and less error-prone.
  • Optimization: The snapshot list is fetched eagerly on mount. That adds a request every time the canvas loads, even if the user never opens the snapshot menu. Lazy-loading on first open would be cleaner.

Open questions:

  • Should restoring a snapshot also restore the selected model, or is it only supposed to restore canvas layers within the currently active model context?
  • Should snapshot restore be treated as a new baseline state, or is it intentional that Undo can immediately return to the pre-restore canvas?

Review Pass 2: Issues Actually Introduced By This PR

  • High: The new user-management API allows removing or deactivating the last active administrator through PATCH /auth/users/{user_id}. There is a guard against deleting the last admin, but the same invariant is not enforced for update operations, so the system can still end up with zero active admins.
  • High: Canvas snapshot restore can reintroduce a stale bbox.modelBase from the saved snapshot. If the current model changed after the snapshot was created, restoring it can leave the canvas state out of sync with the active model.
  • Medium: The edit-user modal keeps local form state initialized from the first selected user and does not resync when editUser changes. That means opening user A, closing the modal, and then opening user B can show or submit stale values from user A.
  • Low: PATCH /auth/users/{user_id} returns 400 for a nonexistent user, even though the route contract says it should return 404.
  • Low: Snapshot ordering is based on insertion order (rowid DESC) instead of updated_at, so re-saving an existing snapshot name does not move it to the top.

Open questions:

  • Is it expected that an admin can demote or deactivate the final active admin account, or should the “cannot remove the last admin” invariant apply to updates as well as deletes?
  • For the edit-user modal, is the intended UX that each open always reflects the currently selected user from server state, even when the modal component instance is reused?

As an addition:

  • High: Canvas snapshots are not durable if the user clears intermediates. Snapshot save serializes the current canvas state, but canvas image objects only store image references (image_name, width, height), not the image payload itself. Clearing intermediates deletes the backing image files. After that, restoring a snapshot brings back canvas objects that still point to the old image_names, and image loading fails because those images no longer exist. In practice, that means a saved snapshot can become unrecoverable after a routine “Clear intermediates” action.

Open question:

  • Are snapshots intended to survive “Clear intermediates”? If yes, the current approach likely needs to either persist referenced images as non-intermediates, embed durable image data, or warn/refuse when a snapshot depends on intermediate-only images.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api frontend PRs that change frontend files python PRs that change python files python-tests PRs that change python tests services PRs that change app services v6.13.x

Projects

Status: 6.13.x Theme: MODELS

Development

Successfully merging this pull request may close these issues.

4 participants