diff --git a/AGENTS.md b/AGENTS.md
index 69ad6ae..2070468 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -62,6 +62,10 @@ All ISAC data-management editors must follow the same successful-save behavior:
4. Server actions must return the add/edit mode and a unique result token;
client-side reactive handling must consume each token only once to avoid
repeated state updates.
+5. When a successful add keeps an icon-enabled editor open, increment the
+ shared upload reset token so the native file input, generated preview,
+ filename, drag/paste state, validation message, and removal state are fresh
+ for the next record.
Apply this convention whenever an ISAC editor is added or materially changed.
diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md
index 593226b..21509c9 100644
--- a/docs/DESIGN_SYSTEM.md
+++ b/docs/DESIGN_SYSTEM.md
@@ -772,6 +772,12 @@ fixed column to `IconUploadField`, give the record name the flexible dominant
column, and stack both controls at mobile widths. Do not enlarge an upload
drop-zone to fill spare identity-row space.
+Icon-enabled consecutive-entry editors pass a successful-add result token into
+the shared upload control. Advancing that token clears the native file input,
+temporary preview URL, filename, drag/paste feedback, and validation state.
+This reset occurs only after a validated add succeeds; failed saves preserve
+the selected image, and successful edits close the editor normally.
+
The primary `name` field in every ISAC data editor is a header-level input:
46 pixels tall with larger, heavier text. This creates a consistent first
reading point for long registries while keeping secondary metadata compact.
diff --git a/src/lib/components/IconUploadField.svelte b/src/lib/components/IconUploadField.svelte
index 99280ba..ef2c1a3 100644
--- a/src/lib/components/IconUploadField.svelte
+++ b/src/lib/components/IconUploadField.svelte
@@ -2,13 +2,14 @@
import ImageUploadInput from '$lib/components/ImageUploadInput.svelte';
let {
currentUrl = '',
- label = 'ICON'
- }: { currentUrl?: string | null; label?: string } = $props();
+ label = 'ICON',
+ resetToken = 0
+ }: { currentUrl?: string | null; label?: string; resetToken?: string | number } = $props();
diff --git a/src/lib/components/ImageUploadInput.svelte b/src/lib/components/ImageUploadInput.svelte
index 6b2e41c..6fb10a9 100644
--- a/src/lib/components/ImageUploadInput.svelte
+++ b/src/lib/components/ImageUploadInput.svelte
@@ -7,7 +7,8 @@
required = false,
currentUrl = '',
emptyLabel = 'NO IMAGE',
- browseLabel = 'CHOOSE FILE'
+ browseLabel = 'CHOOSE FILE',
+ resetToken = 0
}: {
name: string;
accept: string;
@@ -15,16 +16,38 @@
currentUrl?: string | null;
emptyLabel?: string;
browseLabel?: string;
+ resetToken?: string | number;
} = $props();
- let input: HTMLInputElement;
+ let input: HTMLInputElement | undefined;
let previewUrl = $state('');
let fileName = $state('');
let dragging = $state(false);
let error = $state('');
+ let handledResetToken: string | number | null = null;
+
+ function resetUpload() {
+ if (previewUrl) URL.revokeObjectURL(previewUrl);
+ previewUrl = '';
+ fileName = '';
+ dragging = false;
+ error = '';
+ if (input) input.value = '';
+ }
+
+ $effect(() => {
+ if (handledResetToken === null) {
+ handledResetToken = resetToken;
+ return;
+ }
+ if (resetToken !== handledResetToken) {
+ handledResetToken = resetToken;
+ resetUpload();
+ }
+ });
function setFile(file: File | undefined) {
- if (!file) return;
+ if (!file || !input) return;
if (!file.type.startsWith('image/')) {
error = 'Clipboard or dropped content must be an image.';
return;
@@ -108,7 +131,7 @@
{fileName || 'OR USE AN UPLOAD METHOD'}