From f1d3e3c5b5c6c676a8af0dcdc190a1ce38d433a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Sat, 23 May 2026 11:10:11 +0300 Subject: [PATCH] fix: complete progress log localization and resolve background customizer layering bug --- renderer.js | 276 ++++++++++++++++++++++++++++++++++++++++++++-------- styles.css | 6 +- 2 files changed, 243 insertions(+), 39 deletions(-) diff --git a/renderer.js b/renderer.js index d578809..4d268d7 100644 --- a/renderer.js +++ b/renderer.js @@ -182,7 +182,7 @@ function resetWorkspaceToEmpty() { // Clear background customizations resetBgSettings(); - showToast("Image Changed", "Workspace cleared. You can now load a new image.", "info"); + showToast(getTrans('toastImageChanged'), getTrans('toastImageChangedMsg'), "info"); } // --- PYTHON VENV INSTALLER ENGINE --- @@ -208,40 +208,55 @@ elements.btnStartSetup.addEventListener('click', async () => { }; setStepActive(elements.stepVenv); + elements.setupStatusText.innerText = getTrans('setupCreatingVenv'); // Listen for pipeline setup output logs window.api.onSetupProgress((progressText) => { - elements.setupStatusText.innerText = progressText; - - // Parse step state + // Translate setup output progress text if (progressText.includes("Creating Python virtual environment")) { + elements.setupStatusText.innerText = getTrans('setupCreatingVenv'); setStepActive(elements.stepVenv); elements.setupProgressBar.style.width = '15%'; - } else if (progressText.includes("Virtual environment successfully created") || progressText.includes("Virtual environment already exists")) { + } else if (progressText.includes("Virtual environment successfully created")) { + elements.setupStatusText.innerText = getTrans('setupVenvCreated'); + setStepDone(elements.stepVenv); + setStepActive(elements.stepPip); + elements.setupProgressBar.style.width = '30%'; + } else if (progressText.includes("Virtual environment already exists")) { + elements.setupStatusText.innerText = getTrans('setupVenvExists'); setStepDone(elements.stepVenv); setStepActive(elements.stepPip); elements.setupProgressBar.style.width = '30%'; } else if (progressText.includes("Upgrading pip")) { + elements.setupStatusText.innerText = getTrans('setupUpgradingPip'); setStepActive(elements.stepPip); elements.setupProgressBar.style.width = '45%'; } else if (progressText.includes("Installing PyTorch")) { + elements.setupStatusText.innerText = getTrans('setupInstallingTorch'); setStepDone(elements.stepPip); setStepActive(elements.stepDeps); elements.setupProgressBar.style.width = '60%'; - } else if (progressText.includes("Downloading torch") || progressText.includes("Installing collected packages")) { + } else if (progressText.includes("Downloading torch")) { + elements.setupStatusText.innerText = getTrans('setupDownloadingTorch'); elements.setupProgressBar.style.width = '75%'; + } else if (progressText.includes("Installing collected packages")) { + elements.setupStatusText.innerText = getTrans('setupInstallingDeps'); + elements.setupProgressBar.style.width = '85%'; } else if (progressText.includes("Setup completed successfully")) { + elements.setupStatusText.innerText = getTrans('setupCompleted'); setStepDone(elements.stepDeps); setStepDone(elements.stepLibs); elements.setupProgressBar.style.width = '100%'; + } else { + elements.setupStatusText.innerText = progressText; } }); try { const result = await window.api.runPythonSetup(); if (result.code === 0) { - elements.setupStatusText.innerText = "Configuration Complete! Enjoy background removal."; - showToast("Setup Success", "Python environment is fully configured.", "success"); + elements.setupStatusText.innerText = getTrans('setupCompleted'); + showToast(getTrans('toastSetupSuccess'), getTrans('toastSetupSuccessMsg'), "success"); setTimeout(() => { elements.setupOverlay.classList.add('hidden'); @@ -249,14 +264,14 @@ elements.btnStartSetup.addEventListener('click', async () => { }, 1500); } else { setStepFailed(elements.stepDeps); - elements.setupStatusText.innerText = "Setup failed. Check system dependencies."; + elements.setupStatusText.innerText = getTrans('setupFailed'); elements.btnStartSetup.disabled = false; - showToast("Setup Error", "Failed to build Python environment.", "error"); + showToast(getTrans('toastSetupError'), getTrans('toastSetupErrorMsg'), "error"); } } catch (err) { - elements.setupStatusText.innerText = `Error: ${err.message}`; + elements.setupStatusText.innerText = `${getTrans('toastSetupError')}: ${err.message}`; elements.btnStartSetup.disabled = false; - showToast("Setup Error", err.message, "error"); + showToast(getTrans('toastSetupError'), err.message, "error"); } }); @@ -551,7 +566,39 @@ const TRANSLATIONS = { dragDropSub: "Supports PNG, JPG, JPEG, or WebP formats", orText: "OR", browseFiles: "Browse Local Files", - autoTrim: "Auto-Trim" + autoTrim: "Auto-Trim", + statusInit: "Initializing AI model...", + statusWeights: "Loading neural weights into memory...", + statusImporting: "Importing libraries...", + statusDetecting: "Detecting device...", + statusUsingDevice: "Using device: ", + statusLoadingModel: "Loading BiRefNet model...", + statusPreprocessing: "Loading and preprocessing image...", + statusInference: "Running background removal inference...", + statusGeneratingMask: "Generating transparency mask...", + statusSaving: "Saving output transparent image...", + statusDone: "Completed!", + subFirstRun: "This downloads ~200MB of weights on first run.", + subInference: "Extracting details and soft matting alpha...", + subSaving: "Assembling high-res RGBA container...", + subDefault: "Please wait while the AI model processes...", + setupReady: "Ready to configure environment.", + setupCreatingVenv: "Creating Python virtual environment...", + setupVenvCreated: "Virtual environment successfully created.", + setupVenvExists: "Virtual environment already exists.", + setupUpgradingPip: "Upgrading Python package installer (pip)...", + setupInstallingTorch: "Installing PyTorch AI Engine (CPU version)...", + setupDownloadingTorch: "Downloading torch and dependencies...", + setupInstallingDeps: "Installing collected packages...", + setupCompleted: "Configuration Complete! Enjoy background removal.", + setupFailed: "Setup failed. Check system dependencies.", + exportingMasterpiece: "Exporting masterpiece...", + toastExportSuccess: "Export Successful", + toastExportSuccessMsg: "Saved file correctly: ", + toastExportFailed: "Export Failed", + toastExportFailedMsg: "Save operation failed.", + toastExportError: "Export Error", + showInExplorer: "Show in File Explorer" }, tr: { title: "Dekupai - Trunçgil AI Dekupaj", @@ -603,7 +650,39 @@ const TRANSLATIONS = { dragDropSub: "PNG, JPG, JPEG veya WebP formatlarını destekler", orText: "VEYA", browseFiles: "Yerel Dosyalara Göz Atın", - autoTrim: "Oto-Kırp" + autoTrim: "Oto-Kırp", + statusInit: "AI modeli başlatılıyor...", + statusWeights: "Yapay sinir ağları belleğe yükleniyor...", + statusImporting: "Kütüphaneler içe aktarılıyor...", + statusDetecting: "Donanım tespit ediliyor...", + statusUsingDevice: "Kullanılan donanım: ", + statusLoadingModel: "BiRefNet modeli yükleniyor...", + statusPreprocessing: "Görsel yükleniyor ve ön işleme yapılıyor...", + statusInference: "Arka plan temizleme algoritması çalıştırılıyor...", + statusGeneratingMask: "Şeffaflık maskesi oluşturuluyor...", + statusSaving: "Şeffaf görsel kaydediliyor...", + statusDone: "Tamamlandı!", + subFirstRun: "İlk çalıştırmada ~200MB ağırlık dosyası indirilir.", + subInference: "Detaylar ve yumuşak matlaştırma katmanı çıkarılıyor...", + subSaving: "Yüksek çözünürlüklü RGBA görsel birleştiriliyor...", + subDefault: "Yapay zeka modeli işleme yaparken lütfen bekleyin...", + setupReady: "Ortamı yapılandırmaya hazır.", + setupCreatingVenv: "Python sanal ortamı oluşturuluyor...", + setupVenvCreated: "Sanal ortam başarıyla oluşturuldu.", + setupVenvExists: "Sanal ortam zaten mevcut.", + setupUpgradingPip: "Python paket yükleyici (pip) güncelleniyor...", + setupInstallingTorch: "PyTorch AI motoru kuruluyor (CPU versiyonu)...", + setupDownloadingTorch: "Torch ve bağımlılıklar indiriliyor...", + setupInstallingDeps: "Toplanan paketler kuruluyor...", + setupCompleted: "Yapılandırma Tamamlandı! Arka plan temizlemenin keyfini çıkarın.", + setupFailed: "Kurulum başarısız oldu. Sistem bağımlılıklarını kontrol edin.", + exportingMasterpiece: "Çalışma dışa aktarılıyor...", + toastExportSuccess: "Dışa Aktarma Başarılı", + toastExportSuccessMsg: "Dosya başarıyla kaydedildi: ", + toastExportFailed: "Dışa Aktarma Başarısız", + toastExportFailedMsg: "Kaydetme işlemi başarısız oldu.", + toastExportError: "Dışa Aktarma Hatası", + showInExplorer: "Dosya Gezgininde Göster" }, de: { title: "Dekupai - Trunçgil AI Dekupage", @@ -649,13 +728,45 @@ const TRANSLATIONS = { stepLibs: "Installation der HuggingFace Transformers & PIL Bibliothek", creditsVision: "Entwickelt unter der Vision von", creditsExpertise: "durch die Software-Expertise von", - creditsDesc: "Dekupai ist ein fortschrittlicher Deep-Learning-Desktop-Arbeitsbereich, der eine lokale, hochpräzise Hintergrundentfernung mithilfe von BiRefNet-Segmentierungsnetzwerken bietet.", + creditsDesc: "Dekupai is ein fortschrittlicher Deep-Learning-Desktop-Arbeitsbereich, der eine lokale, hochpräzise Hintergrundentfernung mithilfe von BiRefNet-Segmentierungsnetzwerken bietet.", closeCredits: "Schließen", dragDropText: "Bild hierher ziehen und ablegen", dragDropSub: "Unterstützt PNG-, JPG-, JPEG- oder WebP-Formate", orText: "ODER", browseFiles: "Lokale Dateien durchsuchen", - autoTrim: "Auto-Trim" + autoTrim: "Auto-Trim", + statusInit: "AI-Modell wird initialisiert...", + statusWeights: "Neuronale Gewichte werden geladen...", + statusImporting: "Bibliotheken werden importiert...", + statusDetecting: "Hardware wird erkannt...", + statusUsingDevice: "Verwendetes Gerät: ", + statusLoadingModel: "BiRefNet-Modell wird geladen...", + statusPreprocessing: "Bild wird geladen und vorverarbeitet...", + statusInference: "Hintergrundentfernungs-Inferenz wird ausgeführt...", + statusGeneratingMask: "Transparenzmaske wird generiert...", + statusSaving: "Transparentes Ausgabebild wird gespeichert...", + statusDone: "Abgeschlossen!", + subFirstRun: "Erster Start lädt Modell herunter (~200MB)", + subInference: "Details und weiche Maske werden extrahiert...", + subSaving: "Hochauflösender RGBA-Container wird zusammengestellt...", + subDefault: "Bitte warten Sie, während das AI-Modell verarbeitet...", + setupReady: "Bereit zum Konfigurieren der Umgebung.", + setupCreatingVenv: "Isolierte virtuelle Umgebung (.venv) wird erstellt...", + setupVenvCreated: "Virtuelle Umgebung erfolgreich erstellt.", + setupVenvExists: "Virtuelle Umgebung existiert bereits.", + setupUpgradingPip: "Python-Paketinstallateur (pip) wird aktualisiert...", + setupInstallingTorch: "PyTorch AI-Engine (CPU-Version) wird installiert...", + setupDownloadingTorch: "Torch und Abhängigkeiten werden heruntergeladen...", + setupInstallingDeps: "Gesammelte Pakete werden installiert...", + setupCompleted: "Konfiguration abgeschlossen! Viel Spaß mit der Hintergrundentfernung.", + setupFailed: "Setup fehlgeschlagen. Systemabhängigkeiten prüfen.", + exportingMasterpiece: "Meisterwerk wird exportiert...", + toastExportSuccess: "Export erfolgreich", + toastExportSuccessMsg: "Datei erfolgreich gespeichert: ", + toastExportFailed: "Export fehlgeschlagen", + toastExportFailedMsg: "Speichervorgang fehlgeschlagen.", + toastExportError: "Exportfehler", + showInExplorer: "Im Datei-Explorer anzeigen" }, ru: { title: "Dekupai - Trunçgil AI Декупаж", @@ -707,7 +818,39 @@ const TRANSLATIONS = { dragDropSub: "Поддерживает форматы PNG, JPG, JPEG или WebP", orText: "ИЛИ", browseFiles: "Обзор локальных файлов", - autoTrim: "Автообрезка" + autoTrim: "Автообрезка", + statusInit: "Инициализация модели ИИ...", + statusWeights: "Загрузка весов нейросети в память...", + statusImporting: "Импорт библиотек...", + statusDetecting: "Обнаружение устройства...", + statusUsingDevice: "Используемое устройство: ", + statusLoadingModel: "Загрузка модели BiRefNet...", + statusPreprocessing: "Загрузка и предобработка изображения...", + statusInference: "Выполнение удаления фона...", + statusGeneratingMask: "Генерация маски прозрачности...", + statusSaving: "Сохранение выходного прозрачного изображения...", + statusDone: "Готово!", + subFirstRun: "Первый запуск загружает модель (~200 МБ)", + subInference: "Извлечение деталей и мягкой маски альфа...", + subSaving: "Сборка RGBA-контейнера высокого разрешения...", + subDefault: "Пожалуйста, подождите, пока модель ИИ обрабатывает...", + setupReady: "Готов к настройке среды.", + setupCreatingVenv: "Создание изолированной виртуальной среды (.venv)...", + setupVenvCreated: "Виртуальная среда успешно создана.", + setupVenvExists: "Виртуальная среда уже существует.", + setupUpgradingPip: "Обновление установщика пакетов Python (pip)...", + setupInstallingTorch: "Установка движка ИИ PyTorch (версия CPU)...", + setupDownloadingTorch: "Скачивание torch и зависимостей...", + setupInstallingDeps: "Установка собранных пакетов...", + setupCompleted: "Настройка завершена! Наслаждайтесь удалением фона.", + setupFailed: "Ошибка настройки. Проверьте системные зависимости.", + exportingMasterpiece: "Экспорт шедевра...", + toastExportSuccess: "Экспорт успешен", + toastExportSuccessMsg: "Файл успешно сохранен: ", + toastExportFailed: "Ошибка экспорта", + toastExportFailedMsg: "Операция сохранения не удалась.", + toastExportError: "Ошибка экспорта", + showInExplorer: "Показать в проводнике" }, ar: { title: "Dekupai - Trunçgil AI إزالة الخلفية", @@ -759,7 +902,39 @@ const TRANSLATIONS = { dragDropSub: "يدعم صيغ PNG أو JPG أو JPEG أو WebP", orText: "أو", browseFiles: "تصفح الملفات المحلية", - autoTrim: "قص تلقائي" + autoTrim: "قص تلقائي", + statusInit: "تهيئة نموذج الذكاء الاصطناعي...", + statusWeights: "تحميل أوزان الشبكة العصبية في الذاكرة...", + statusImporting: "استيراد المكتبات...", + statusDetecting: "الكشف عن الجهاز...", + statusUsingDevice: "الجهاز المستخدم: ", + statusLoadingModel: "تحميل نموذج BiRefNet...", + statusPreprocessing: "تحميل ومعالجة الصورة مسبقاً...", + statusInference: "تشغيل خوارزمية إزالة الخلفية...", + statusGeneratingMask: "إنشاء قناع الشفافية...", + statusSaving: "حفظ الصورة الشفافة الناتجة...", + statusDone: "اكتمل!", + subFirstRun: "التشغيل الأول يقوم بتنزيل النموذج (~200 ميجابايت)", + subInference: "استخراج التفاصيل والشفافية الناعمة...", + subSaving: "تجميع حاوية RGBA عالية الدقة...", + subDefault: "يرجى الانتظار أثناء معالجة نموذج الذكاء الاصطناعي...", + setupReady: "جاهز لتكوين البيئة.", + setupCreatingVenv: "إنشاء بيئة افتراضية معزولة (.venv)...", + setupVenvCreated: "تم إنشاء البيئة الافتراضية بنجاح.", + setupVenvExists: "البيئة الافتراضية موجودة بالفعل.", + setupUpgradingPip: "ترقية مثبت حزم بايثون (pip)...", + setupInstallingTorch: "تثبيت محرك الذكاء الاصطناعي PyTorch (نسخة CPU)...", + setupDownloadingTorch: "تنزيل torch والاعتمادات...", + setupInstallingDeps: "تثبيت الحزم التي تم جمعها...", + setupCompleted: "اكتمل التكوين! استمتع بإزالة الخلفية.", + setupFailed: "فشل الإعداد. تحقق من تبعيات النظام.", + exportingMasterpiece: "تصدير العمل الفني...", + toastExportSuccess: "تم التصدير بنجاح", + toastExportSuccessMsg: "تم حفظ الملف بنجاح: ", + toastExportFailed: "فشل التصدير", + toastExportFailedMsg: "فشلت عملية الحفظ.", + toastExportError: "خطأ في التصدير", + showInExplorer: "عرض في مستكشف الملفات" } }; @@ -862,8 +1037,8 @@ async function executeBackgroundRemoval() { elements.btnRemoveBg.classList.add('hidden'); elements.loaderBox.classList.remove('hidden'); - elements.loaderText.innerText = "Initializing AI model..."; - elements.loaderSubtext.innerText = "Loading neural weights into memory..."; + elements.loaderText.innerText = getTrans('statusInit'); + elements.loaderSubtext.innerText = getTrans('statusWeights'); // Get unique temporary output file path const tempOut = await window.api.getTempPath('transparent_result.png'); @@ -871,15 +1046,40 @@ async function executeBackgroundRemoval() { // Listen to execution progress window.api.onProcessProgress((progress) => { if (progress.includes("[STATUS]")) { - const statusText = progress.replace("[STATUS] ", ""); - elements.loaderText.innerText = statusText; + const statusText = progress.replace("[STATUS] ", "").trim(); - if (statusText.includes("Hugging Face")) { - elements.loaderSubtext.innerText = "This downloads ~200MB of weights on first run."; - } else if (statusText.includes("inference")) { - elements.loaderSubtext.innerText = "Extracting details and soft matting alpha..."; - } else if (statusText.includes("Saving")) { - elements.loaderSubtext.innerText = "Assembling high-res RGBA container..."; + // Let's map statusText to localized keys + if (statusText.includes("Importing libraries")) { + elements.loaderText.innerText = getTrans('statusImporting'); + elements.loaderSubtext.innerText = getTrans('subDefault'); + } else if (statusText.includes("Detecting device")) { + elements.loaderText.innerText = getTrans('statusDetecting'); + elements.loaderSubtext.innerText = getTrans('subDefault'); + } else if (statusText.startsWith("Using device:")) { + const device = statusText.replace("Using device:", "").trim(); + elements.loaderText.innerText = getTrans('statusUsingDevice') + device; + elements.loaderSubtext.innerText = getTrans('subDefault'); + } else if (statusText.includes("Loading BiRefNet model")) { + elements.loaderText.innerText = getTrans('statusLoadingModel'); + elements.loaderSubtext.innerText = getTrans('subFirstRun'); + } else if (statusText.includes("Loading and preprocessing image")) { + elements.loaderText.innerText = getTrans('statusPreprocessing'); + elements.loaderSubtext.innerText = getTrans('subDefault'); + } else if (statusText.includes("Running background removal inference")) { + elements.loaderText.innerText = getTrans('statusInference'); + elements.loaderSubtext.innerText = getTrans('subInference'); + } else if (statusText.includes("Generating transparency mask")) { + elements.loaderText.innerText = getTrans('statusGeneratingMask'); + elements.loaderSubtext.innerText = getTrans('subInference'); + } else if (statusText.includes("Saving output transparent image")) { + elements.loaderText.innerText = getTrans('statusSaving'); + elements.loaderSubtext.innerText = getTrans('subSaving'); + } else if (statusText.includes("DONE")) { + elements.loaderText.innerText = getTrans('statusDone'); + elements.loaderSubtext.innerText = ""; + } else { + // Fallback for any other status logs + elements.loaderText.innerText = statusText; } } }); @@ -889,7 +1089,7 @@ async function executeBackgroundRemoval() { if (result.success) { processedTempPath = result.outputPath; - showToast("AI Success", "Image background removed successfully!", "success"); + showToast(getTrans('toastTitleSuccess'), getTrans('toastTrimCompleteMsg'), "success"); // Update workspace preview image source to output transparent PNG // Add timestamp to bypass browser cache refreshing @@ -909,10 +1109,10 @@ async function executeBackgroundRemoval() { }, 300); } else { - showToast("Inference Error", result.error || "Execution terminated unexpectedly.", "error"); + showToast(getTrans('toastTitleError'), result.error || getTrans('toastSetupErrorMsg'), "error"); } } catch (err) { - showToast("AI Fatal Error", err.message, "error"); + showToast(getTrans('toastTitleError'), err.message, "error"); } finally { elements.loaderBox.classList.add('hidden'); elements.btnRemoveBg.classList.remove('hidden'); @@ -1006,7 +1206,7 @@ async function executeExport() { elements.btnExport.disabled = true; elements.btnExport.innerHTML = ` - Exporting masterpiece... + ${getTrans('exportingMasterpiece')} `; setTimeout(async () => { @@ -1069,22 +1269,22 @@ async function executeExport() { if (result.success) { showToastWithAction( - "Export Successful", - `Saved file correctly: ${result.filePath.split(/[\\/]/).pop()}`, + getTrans('toastExportSuccess'), + `${getTrans('toastExportSuccessMsg')}${result.filePath.split(/[\\/]/).pop()}`, "success", result.filePath ); } else if (!result.canceled) { - showToast("Export Failed", result.error || "Save operation failed.", "error"); + showToast(getTrans('toastExportFailed'), result.error || getTrans('toastExportFailedMsg'), "error"); } } catch (err) { - showToast("Export Error", err.message, "error"); + showToast(getTrans('toastExportError'), err.message, "error"); } finally { // Re-enable button elements.btnExport.disabled = false; elements.btnExport.innerHTML = ` - Export Masterpiece + ${getTrans('exportMasterpiece')} `; } }, 100); @@ -1118,7 +1318,7 @@ function showToastWithAction(title, message, type = 'success', filePath) { hideToast(); elements.toastTitle.innerText = title; - elements.toastMessage.innerHTML = `${message}
`; + elements.toastMessage.innerHTML = `${message}
`; elements.toastBanner.className = 'toast-notification glass-panel-inner'; if (type === 'success') elements.toastBanner.classList.add('success'); diff --git a/styles.css b/styles.css index 22854a6..9c86eb3 100644 --- a/styles.css +++ b/styles.css @@ -348,13 +348,17 @@ body { left: 0; right: 0; bottom: 0; - z-index: 1; + z-index: 1 !important; pointer-events: none; opacity: 0; transition: opacity 0.3s ease; } /* CropperJS Custom Visual Styling overrides */ +.cropper-container { + z-index: 2 !important; +} + .cropper-view-box { outline: 2px solid var(--accent-purple); outline-color: var(--accent-purple);