diff --git a/apps/web/src/components/common/dropzone.tsx b/apps/web/src/components/common/dropzone.tsx index 0ecc0e3d..b82c0c01 100644 --- a/apps/web/src/components/common/dropzone.tsx +++ b/apps/web/src/components/common/dropzone.tsx @@ -1,4 +1,4 @@ -import { FileImage, FileUp, Upload } from "lucide-react"; +import { AlertCircle, FileImage, FileUp, Upload } from "lucide-react"; import { type DragEvent, useCallback, useEffect, useState } from "react"; import { useTranslation } from "@/contexts/i18n-context"; import { useUrlImport } from "@/hooks/use-url-import"; @@ -116,11 +116,19 @@ export function Dropzone({ const checkFile = fileFilter ?? isImageFile; const resolvedAccept = expandAccept(accept); const [isDragging, setIsDragging] = useState(false); + const [error, setError] = useState(null); const [urlInput, setUrlInput] = useState(""); const [urlLoading, setUrlLoading] = useState(false); const [urlError, setUrlError] = useState(null); const [showBulkModal, setShowBulkModal] = useState(false); + useEffect(() => { + if (error) { + const timer = setTimeout(() => setError(null), 5000); + return () => clearTimeout(timer); + } + }, [error]); + const { importSingleUrl } = useUrlImport(); const handleUrlSubmit = useCallback(async () => { @@ -154,20 +162,32 @@ export function Dropzone({ e.preventDefault(); e.stopPropagation(); setIsDragging(false); - const files = Array.from(e.dataTransfer.files).filter(checkFile); - if (files.length > 0) onFiles?.(files); + setError(null); + const droppedFiles = Array.from(e.dataTransfer.files); + const validFiles = droppedFiles.filter(checkFile); + if (validFiles.length > 0) { + onFiles?.(validFiles); + } else if (droppedFiles.length > 0) { + setError(acceptDescription || `This tool accepts ${accept || "image files"}`); + } }, - [onFiles, checkFile], + [onFiles, checkFile, acceptDescription, accept], ); const handleClick = () => { + setError(null); const input = document.createElement("input"); input.type = "file"; input.multiple = multiple; if (resolvedAccept) input.accept = resolvedAccept; input.onchange = (e) => { - const files = Array.from((e.target as HTMLInputElement).files || []).filter(checkFile); - if (files.length > 0) onFiles?.(files); + const picked = Array.from((e.target as HTMLInputElement).files || []); + const validFiles = picked.filter(checkFile); + if (validFiles.length > 0) { + onFiles?.(validFiles); + } else if (picked.length > 0) { + setError(acceptDescription || `This tool accepts ${accept || "image files"}`); + } }; input.click(); }; @@ -260,6 +280,13 @@ export function Dropzone({ {acceptDescription ?? t.dropzone.defaultFormats}

+ {error && ( +

+ + {error} +

+ )} + {!compact && onUrlImport && ( <>
diff --git a/apps/web/src/pages/tool-page.tsx b/apps/web/src/pages/tool-page.tsx index 8f21c9a0..47ff33dd 100644 --- a/apps/web/src/pages/tool-page.tsx +++ b/apps/web/src/pages/tool-page.tsx @@ -1,5 +1,6 @@ import { MODALITIES, PYTHON_SIDECAR_TOOLS, TOOL_BUNDLE_MAP, TOOLS } from "@snapotter/shared"; import { + AlertCircle, CheckCircle2, ChevronLeft, ChevronRight, @@ -617,10 +618,30 @@ export function ToolPage() { // which also match !hasProcessed and would show the canvas instead of the error) if (hasFile && !hasProcessed && currentEntry?.status === "failed") { return ( -
-

- {currentEntry.error ?? t.toolPage.processingFailed} -

+
+
+ +

+ {currentEntry.error || t.toolPage.processingFailed} +

+

{t.toolPage.settingsSaved}

+
+ + +
+
); } diff --git a/packages/shared/src/i18n/ar.ts b/packages/shared/src/i18n/ar.ts index 92bc051e..ae38557a 100644 --- a/packages/shared/src/i18n/ar.ts +++ b/packages/shared/src/i18n/ar.ts @@ -2453,6 +2453,9 @@ export const ar: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "تم حفظ إعداداتك.", + tryAgain: "حاول مرة أخرى", + tryDifferentFile: "جرب ملفًا مختلفًا", }, homePage: { generatingPreview: "جاري إنشاء المعاينة...", diff --git a/packages/shared/src/i18n/de.ts b/packages/shared/src/i18n/de.ts index ecb33845..974306c1 100644 --- a/packages/shared/src/i18n/de.ts +++ b/packages/shared/src/i18n/de.ts @@ -2467,6 +2467,9 @@ export const de: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Ihre Einstellungen wurden gespeichert.", + tryAgain: "Erneut versuchen", + tryDifferentFile: "Andere Datei versuchen", }, homePage: { generatingPreview: "Vorschau wird generiert...", diff --git a/packages/shared/src/i18n/en.ts b/packages/shared/src/i18n/en.ts index 8bf8b7df..8fc4061b 100644 --- a/packages/shared/src/i18n/en.ts +++ b/packages/shared/src/i18n/en.ts @@ -2417,6 +2417,9 @@ export const en = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Your settings are saved.", + tryAgain: "Try again", + tryDifferentFile: "Try a different file", }, homePage: { generatingPreview: "Generating preview...", diff --git a/packages/shared/src/i18n/es.ts b/packages/shared/src/i18n/es.ts index cad20c82..5b7551f1 100644 --- a/packages/shared/src/i18n/es.ts +++ b/packages/shared/src/i18n/es.ts @@ -2449,6 +2449,9 @@ export const es: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Tu configuracion se ha guardado.", + tryAgain: "Reintentar", + tryDifferentFile: "Probar con otro archivo", }, homePage: { generatingPreview: "Generando vista previa...", diff --git a/packages/shared/src/i18n/fr.ts b/packages/shared/src/i18n/fr.ts index d6e61f98..d24a2e80 100644 --- a/packages/shared/src/i18n/fr.ts +++ b/packages/shared/src/i18n/fr.ts @@ -2468,6 +2468,9 @@ export const fr: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Vos parametres sont enregistres.", + tryAgain: "Reessayer", + tryDifferentFile: "Essayer un autre fichier", }, homePage: { generatingPreview: "Generation de l'apercu...", diff --git a/packages/shared/src/i18n/hi.ts b/packages/shared/src/i18n/hi.ts index f8dfb451..8bc17b44 100644 --- a/packages/shared/src/i18n/hi.ts +++ b/packages/shared/src/i18n/hi.ts @@ -2450,6 +2450,9 @@ export const hi: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "आपकी सेटिंग्स सहेज ली गई हैं।", + tryAgain: "पुनः प्रयास करें", + tryDifferentFile: "एक अलग फ़ाइल आज़माएं", }, homePage: { generatingPreview: "प्रीव्यू जनरेट हो रहा है...", diff --git a/packages/shared/src/i18n/id.ts b/packages/shared/src/i18n/id.ts index 7a0420bc..f6a6277f 100644 --- a/packages/shared/src/i18n/id.ts +++ b/packages/shared/src/i18n/id.ts @@ -2462,6 +2462,9 @@ export const id: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Pengaturan Anda tersimpan.", + tryAgain: "Coba lagi", + tryDifferentFile: "Coba file lain", }, homePage: { generatingPreview: "Membuat pratinjau...", diff --git a/packages/shared/src/i18n/it.ts b/packages/shared/src/i18n/it.ts index b3711766..255386fd 100644 --- a/packages/shared/src/i18n/it.ts +++ b/packages/shared/src/i18n/it.ts @@ -2461,6 +2461,9 @@ export const it: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Le impostazioni sono state salvate.", + tryAgain: "Riprova", + tryDifferentFile: "Prova un file diverso", }, homePage: { generatingPreview: "Generazione anteprima...", diff --git a/packages/shared/src/i18n/ja.ts b/packages/shared/src/i18n/ja.ts index a6dca42a..d491ad51 100644 --- a/packages/shared/src/i18n/ja.ts +++ b/packages/shared/src/i18n/ja.ts @@ -2420,6 +2420,9 @@ export const ja: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "設定は保存されています。", + tryAgain: "再試行", + tryDifferentFile: "別のファイルを試す", }, homePage: { generatingPreview: "プレビューを生成中...", diff --git a/packages/shared/src/i18n/ko.ts b/packages/shared/src/i18n/ko.ts index e85133ec..21aee091 100644 --- a/packages/shared/src/i18n/ko.ts +++ b/packages/shared/src/i18n/ko.ts @@ -2405,6 +2405,9 @@ export const ko: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "설정이 저장되었습니다.", + tryAgain: "다시 시도", + tryDifferentFile: "다른 파일 시도", }, homePage: { generatingPreview: "미리보기 생성 중...", diff --git a/packages/shared/src/i18n/nl.ts b/packages/shared/src/i18n/nl.ts index d00f181a..cf516d06 100644 --- a/packages/shared/src/i18n/nl.ts +++ b/packages/shared/src/i18n/nl.ts @@ -2464,6 +2464,9 @@ export const nl: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Uw instellingen zijn opgeslagen.", + tryAgain: "Opnieuw proberen", + tryDifferentFile: "Een ander bestand proberen", }, homePage: { generatingPreview: "Preview genereren...", diff --git a/packages/shared/src/i18n/pl.ts b/packages/shared/src/i18n/pl.ts index 5c444f6f..1653f20f 100644 --- a/packages/shared/src/i18n/pl.ts +++ b/packages/shared/src/i18n/pl.ts @@ -2465,6 +2465,9 @@ export const pl: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Twoje ustawienia zostaly zapisane.", + tryAgain: "Sprobuj ponownie", + tryDifferentFile: "Sprobuj inny plik", }, homePage: { generatingPreview: "Generowanie podglądu...", diff --git a/packages/shared/src/i18n/pt-BR.ts b/packages/shared/src/i18n/pt-BR.ts index 2f3f9366..24ea07cc 100644 --- a/packages/shared/src/i18n/pt-BR.ts +++ b/packages/shared/src/i18n/pt-BR.ts @@ -2461,6 +2461,9 @@ export const ptBR: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Suas configuracoes foram salvas.", + tryAgain: "Tentar novamente", + tryDifferentFile: "Tentar outro arquivo", }, homePage: { generatingPreview: "Gerando visualizacao...", diff --git a/packages/shared/src/i18n/ru.ts b/packages/shared/src/i18n/ru.ts index 9b36ee56..652b3407 100644 --- a/packages/shared/src/i18n/ru.ts +++ b/packages/shared/src/i18n/ru.ts @@ -2463,6 +2463,9 @@ export const ru: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Ваши настройки сохранены.", + tryAgain: "Попробовать снова", + tryDifferentFile: "Попробовать другой файл", }, homePage: { generatingPreview: "Генерация предпросмотра...", diff --git a/packages/shared/src/i18n/sv.ts b/packages/shared/src/i18n/sv.ts index 6a3fa4ef..4f1b2078 100644 --- a/packages/shared/src/i18n/sv.ts +++ b/packages/shared/src/i18n/sv.ts @@ -2460,6 +2460,9 @@ export const sv: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Dina installningar ar sparade.", + tryAgain: "Forsok igen", + tryDifferentFile: "Prova en annan fil", }, homePage: { generatingPreview: "Genererar forhandsvisning...", diff --git a/packages/shared/src/i18n/th.ts b/packages/shared/src/i18n/th.ts index 1a3c4cf7..e59755e2 100644 --- a/packages/shared/src/i18n/th.ts +++ b/packages/shared/src/i18n/th.ts @@ -2442,6 +2442,9 @@ export const th: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "บันทึกการตั้งค่าของคุณแล้ว", + tryAgain: "ลองอีกครั้ง", + tryDifferentFile: "ลองไฟล์อื่น", }, homePage: { generatingPreview: "กำลังสร้างตัวอย่าง...", diff --git a/packages/shared/src/i18n/tr.ts b/packages/shared/src/i18n/tr.ts index 483ab482..f9feb4f3 100644 --- a/packages/shared/src/i18n/tr.ts +++ b/packages/shared/src/i18n/tr.ts @@ -2465,6 +2465,9 @@ export const tr: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Ayarlariniz kaydedildi.", + tryAgain: "Tekrar dene", + tryDifferentFile: "Farkli bir dosya dene", }, homePage: { generatingPreview: "Önizleme oluşturuluyor...", diff --git a/packages/shared/src/i18n/uk.ts b/packages/shared/src/i18n/uk.ts index fed29ec0..8ca53c59 100644 --- a/packages/shared/src/i18n/uk.ts +++ b/packages/shared/src/i18n/uk.ts @@ -2463,6 +2463,9 @@ export const uk: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Ваші налаштування збережено.", + tryAgain: "Спробувати знову", + tryDifferentFile: "Спробувати інший файл", }, homePage: { generatingPreview: "Генерація попереднього перегляду...", diff --git a/packages/shared/src/i18n/vi.ts b/packages/shared/src/i18n/vi.ts index 0ceaecef..6eabf0f7 100644 --- a/packages/shared/src/i18n/vi.ts +++ b/packages/shared/src/i18n/vi.ts @@ -2462,6 +2462,9 @@ export const vi: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "Cai dat cua ban da duoc luu.", + tryAgain: "Thu lai", + tryDifferentFile: "Thu tep khac", }, homePage: { generatingPreview: "Đang tạo bản xem trước...", diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts index 5d8f08b7..199490d9 100644 --- a/packages/shared/src/i18n/zh-CN.ts +++ b/packages/shared/src/i18n/zh-CN.ts @@ -2395,6 +2395,9 @@ export const zhCN: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "您的设置已保存。", + tryAgain: "重试", + tryDifferentFile: "尝试其他文件", }, homePage: { generatingPreview: "正在生成预览...", diff --git a/packages/shared/src/i18n/zh-TW.ts b/packages/shared/src/i18n/zh-TW.ts index 04135b0f..56bbffb2 100644 --- a/packages/shared/src/i18n/zh-TW.ts +++ b/packages/shared/src/i18n/zh-TW.ts @@ -2393,6 +2393,9 @@ export const zhTW: TranslationKeys = { saved: "Saved", noChange: "no change", download: "Download", + settingsSaved: "您的設定已儲存。", + tryAgain: "重試", + tryDifferentFile: "嘗試其他檔案", }, homePage: { generatingPreview: "正在產生預覽...",