fix: complete progress log localization and resolve background customizer layering bug

This commit is contained in:
Ümit Tunç
2026-05-23 11:10:11 +03:00
parent 868d7519a4
commit f1d3e3c5b5
2 changed files with 243 additions and 39 deletions
+238 -38
View File
@@ -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 = `
<svg class="loader-ring" viewBox="0 0 50 50" style="width:16px;height:16px;margin:0;"><circle cx="25" cy="25" r="20" fill="none" stroke-width="4" stroke="#fff"></circle></svg>
<span>Exporting masterpiece...</span>
<span>${getTrans('exportingMasterpiece')}</span>
`;
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 = `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
<span>Export Masterpiece</span>
<span>${getTrans('exportMasterpiece')}</span>
`;
}
}, 100);
@@ -1118,7 +1318,7 @@ function showToastWithAction(title, message, type = 'success', filePath) {
hideToast();
elements.toastTitle.innerText = title;
elements.toastMessage.innerHTML = `${message} <br><button id="btn-toast-explore" class="btn btn-secondary btn-sm" style="margin-top:6px;width:100%;">Show in File Explorer</button>`;
elements.toastMessage.innerHTML = `${message} <br><button id="btn-toast-explore" class="btn btn-secondary btn-sm" style="margin-top:6px;width:100%;">${getTrans('showInExplorer')}</button>`;
elements.toastBanner.className = 'toast-notification glass-panel-inner';
if (type === 'success') elements.toastBanner.classList.add('success');