Media Clipboard¶
A persistent Zustand clipboard of media resources for side-by-side comparison. Survives page navigation and supports both server resources and local file uploads.
Setup¶
MediaClipboardFab is rendered globally in ClientProviders. It auto-shows when the store has items. No page-level setup is needed.
Adding Items Programmatically¶
import { useMediaClipboardStore } from "@/store/mediaClipboardStore";
const { addItem, addLocalItem, removeByResourceId, clearAll } =
useMediaClipboardStore();
// Server resource from API
addItem(resource);
// Local file (image only, max 30 MB)
await addLocalItem(file);
// Remove by API resource ID
removeByResourceId(resource.id);
// Clear everything
clearAll();
addItem deduplicates by resource.id — adding the same resource twice is a no-op.
ClipboardItem Type¶
interface ClipboardItem {
clipboardId: string; // UUID assigned at add time
resource: MediaResourceMinimal | MediaResourcePublic;
addedAt: string; // ISO timestamp
isLocal?: boolean; // true for local file uploads
localBlobKey?: string; // key into local blob storage (IndexedDB)
}
Local Uploads¶
- Max size: 30 MB
- Allowed types: images only
- Storage: blobs stored in IndexedDB via
localMediaStorage - Image dimensions are probed automatically on add
isLocal: truemarks the item;localBlobKeymaps to the blobremoveByResourceIdcleans up the blob from storageclearAllpurges all local blobs
Compare Mode¶
The clipboard content cycles through three states:
grid— default gallery of all itemspinned— one item is pinned; clicking another enters compare modecomparing— two items displayed side-by-side; Escape exits back to grid
State is controlled internally by MediaClipboardContent.
FAB Behavior¶
MediaClipboardFab is a fixed-position button in the bottom-right corner:
- Shows a badge with the current item count
- Hidden when the store is empty
- Draggable — drag threshold is 5 px; a move below the threshold is treated as a click
- Click opens the clipboard as a floating window
Gallery Keys¶
Each clipboard item is assigned a unique gallery key so images open in an isolated lightbox:
Store Persistence¶
The store uses Zustand persist middleware:
| Property | Value |
|---|---|
| localStorage key | "media-clipboard" |
| Current schema version | 2 |
| Migration | v1 → v2 adds isLocal and localBlobKey fields |
Source Files¶
| File | Contents |
|---|---|
src/store/mediaClipboardStore.ts |
Zustand store, ClipboardItem type, persistence config |
src/components/MediaClipboard/MediaClipboardFab.tsx |
Draggable FAB |
src/components/MediaClipboard/MediaClipboardContent.tsx |
Grid view + compare mode orchestration |
src/components/MediaClipboard/MediaClipboardCompareView.tsx |
Side-by-side compare layout |