diff --git a/Auto-Image.js b/Auto-Image.js index f7e0f17..45d1402 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -56,6 +56,14 @@ paintingPaused: "⏸️ Pintura pausada na posição X: {x}, Y: {y}", captchaNeeded: "❗ Token CAPTCHA necessário. Pinte um pixel manualmente para continuar.", + saveData: "Salvar Progresso", + loadData: "Carregar Progresso", + autoSaved: "✅ Progresso salvo automaticamente", + dataLoaded: "✅ Progresso carregado com sucesso", + noSavedData: "❌ Nenhum progresso salvo encontrado", + savedDataFound: "✅ Progresso salvo encontrado! Carregar para continuar?", + savedDate: "Salvo em: {date}", + clickLoadToContinue: "Clique em 'Carregar Progresso' para continuar.", }, en: { title: "WPlace Auto-Image", @@ -93,6 +101,14 @@ paintingPaused: "⏸️ Painting paused at position X: {x}, Y: {y}", captchaNeeded: "❗ CAPTCHA token needed. Paint one pixel manually to continue.", + saveData: "Save Progress", + loadData: "Load Progress", + autoSaved: "✅ Progress saved automatically", + dataLoaded: "✅ Progress loaded successfully", + noSavedData: "❌ No saved progress found", + savedDataFound: "✅ Saved progress found! Load to continue?", + savedDate: "Saved on: {date}", + clickLoadToContinue: "Click 'Load Progress' to continue.", }, }; @@ -224,6 +240,149 @@ return result; }, + // Save/Load Progress Functions + saveProgress: () => { + try { + const progressData = { + version: "1.0", + timestamp: Date.now(), + state: { + imageLoaded: state.imageLoaded, + totalPixels: state.totalPixels, + paintedPixels: state.paintedPixels, + lastPosition: state.lastPosition, + startPosition: state.startPosition, + region: state.region, + paintedMap: state.paintedMap, + imageData: state.imageData, + availableColors: state.availableColors, + colorsChecked: state.colorsChecked, + language: state.language + } + }; + + localStorage.setItem('wplace-auto-image-progress', JSON.stringify(progressData)); + console.log('✅ Progress saved successfully'); + return true; + } catch (error) { + console.error('❌ Error saving progress:', error); + return false; + } + }, + + loadProgress: () => { + try { + const savedData = localStorage.getItem('wplace-auto-image-progress'); + if (!savedData) { + return null; + } + + const progressData = JSON.parse(savedData); + + // Validate data structure + if (!progressData.version || !progressData.state) { + return null; + } + + return progressData; + } catch (error) { + console.error('❌ Error loading progress:', error); + return null; + } + }, + + restoreProgress: (progressData) => { + try { + const savedState = progressData.state; + + // Restore state + state.imageLoaded = savedState.imageLoaded; + state.totalPixels = savedState.totalPixels; + state.paintedPixels = savedState.paintedPixels; + state.lastPosition = savedState.lastPosition || { x: 0, y: 0 }; + state.startPosition = savedState.startPosition; + state.region = savedState.region; + state.paintedMap = savedState.paintedMap; + state.imageData = savedState.imageData; + state.availableColors = savedState.availableColors; + state.language = savedState.language; + state.colorsChecked = savedState.availableColors && savedState.availableColors.length > 0; + + // Update UI to reflect restored state + if (state.imageLoaded) { + const initBotBtn = document.querySelector("#initBotBtn"); + const uploadBtn = document.querySelector("#uploadBtn"); + const resizeBtn = document.querySelector("#resizeBtn"); + const selectPosBtn = document.querySelector("#selectPosBtn"); + const startBtn = document.querySelector("#startBtn"); + const saveBtn = document.querySelector("#saveBtn"); + const progressBar = document.querySelector("#progressBar"); + + // Show/hide appropriate buttons based on state + if (state.colorsChecked) { + initBotBtn.style.display = "none"; + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + } else { + initBotBtn.style.display = "block"; + uploadBtn.disabled = true; + selectPosBtn.disabled = true; + } + + resizeBtn.disabled = false; + saveBtn.disabled = false; + + if (state.startPosition && state.region) { + selectPosBtn.disabled = false; + startBtn.disabled = false; + } + + // Update progress bar + const progress = state.totalPixels > 0 ? Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; + progressBar.style.width = `${progress}%`; + + // Update status message based on progress + if (state.paintedPixels > 0) { + if (state.lastPosition.x > 0 || state.lastPosition.y > 0) { + updateUI("paintingPaused", "warning", { + x: state.lastPosition.x, + y: state.lastPosition.y + }); + } else { + updateUI("paintingProgress", "default", { + painted: state.paintedPixels, + total: state.totalPixels, + }); + } + } else { + updateUI("imageLoaded", "success", { count: state.totalPixels }); + } + } + + // Update stats to show current progress + updateStats(); + return true; + } catch (error) { + console.error('❌ Error restoring progress:', error); + return false; + } + }, + + clearProgress: () => { + try { + localStorage.removeItem('wplace-auto-image-progress'); + console.log('✅ Progress data cleared'); + return true; + } catch (error) { + console.error('❌ Error clearing progress:', error); + return false; + } + }, + + hasSavedProgress: () => { + return localStorage.getItem('wplace-auto-image-progress') !== null; + }, + showAlert: (message, type = "info") => { const alert = document.createElement("div"); alert.style.position = "fixed"; @@ -686,6 +845,14 @@ ${Utils.t("stopPainting")} + +
@@ -787,6 +954,8 @@ const selectPosBtn = container.querySelector("#selectPosBtn"); const startBtn = container.querySelector("#startBtn"); const stopBtn = container.querySelector("#stopBtn"); + const saveBtn = container.querySelector("#saveBtn"); + const loadBtn = container.querySelector("#loadBtn"); const minimizeBtn = container.querySelector("#minimizeBtn"); const statusText = container.querySelector("#statusText"); const progressBar = container.querySelector("#progressBar"); @@ -1031,6 +1200,7 @@ state.lastPosition = { x: 0, y: 0 }; resizeBtn.disabled = false; + saveBtn.disabled = false; if (state.startPosition) { startBtn.disabled = false; @@ -1136,6 +1306,7 @@ uploadBtn.disabled = true; selectPosBtn.disabled = true; resizeBtn.disabled = true; + saveBtn.disabled = true; updateUI("startPaintingMsg", "success"); @@ -1146,6 +1317,7 @@ } finally { state.running = false; stopBtn.disabled = true; + saveBtn.disabled = false; if (!state.stopFlag) { startBtn.disabled = true; @@ -1163,7 +1335,86 @@ state.running = false; stopBtn.disabled = true; updateUI("paintingStopped", "warning"); + + // Auto save when stopping + if (state.imageLoaded && state.paintedPixels > 0) { + Utils.saveProgress(); + Utils.showAlert(Utils.t("autoSaved"), "success"); + } }); + + saveBtn.addEventListener("click", () => { + if (!state.imageLoaded) { + Utils.showAlert(Utils.t("missingRequirements"), "error"); + return; + } + + const success = Utils.saveProgress(); + if (success) { + updateUI("autoSaved", "success"); + Utils.showAlert(Utils.t("autoSaved"), "success"); + } else { + Utils.showAlert("❌ Erro ao salvar progresso", "error"); + } + }); + + loadBtn.addEventListener("click", () => { + const savedData = Utils.loadProgress(); + if (!savedData) { + updateUI("noSavedData", "warning"); + Utils.showAlert(Utils.t("noSavedData"), "warning"); + return; + } + + // Show confirmation dialog + const confirmLoad = confirm( + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels` + ); + + if (confirmLoad) { + const success = Utils.restoreProgress(savedData); + if (success) { + updateUI("dataLoaded", "success"); + Utils.showAlert(Utils.t("dataLoaded"), "success"); + + // Enable appropriate buttons + saveBtn.disabled = false; + + // Show init button if colors not checked yet + if (!state.colorsChecked) { + initBotBtn.style.display = "block"; + } + + if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { + startBtn.disabled = false; + } + } else { + Utils.showAlert("❌ Erro ao carregar progresso", "error"); + } + } + }); + + // Check for saved progress on startup + const checkSavedProgress = () => { + const savedData = Utils.loadProgress(); + if (savedData && savedData.state.paintedPixels > 0) { + const savedDate = new Date(savedData.timestamp).toLocaleString(); + const progress = Math.round((savedData.state.paintedPixels / savedData.state.totalPixels) * 100); + + Utils.showAlert( + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${savedDate}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + + `${Utils.t("clickLoadToContinue")}`, + "info" + ); + } + }; + + // Check for saved progress after a short delay to let UI settle + setTimeout(checkSavedProgress, 1000); } async function processImage() { @@ -1240,6 +1491,11 @@ painted: state.paintedPixels, total: state.totalPixels, }); + + // Auto-save progress every 50 pixels + if (state.paintedPixels % 50 === 0) { + Utils.saveProgress(); + } } pixelBatch = []; @@ -1275,10 +1531,14 @@ if (state.stopFlag) { updateUI("paintingStopped", "warning"); + // Save progress when stopped + Utils.saveProgress(); } else { updateUI("paintingComplete", "success", { count: state.paintedPixels }); state.lastPosition = { x: 0, y: 0 }; state.paintedMap = null; + // Clear saved data when completed + Utils.clearProgress(); } updateStats(); @@ -1331,4 +1591,4 @@ } createUI(); -})(); +})(); \ No newline at end of file diff --git a/test-save-load.md b/test-save-load.md new file mode 100644 index 0000000..a953055 --- /dev/null +++ b/test-save-load.md @@ -0,0 +1,38 @@ +# Test Save/Load Progress Feature + +## Các tính năng đã thêm: + +### Auto-Image.js: +1. **Save Progress** - Nút lưu tiến độ thủ công +2. **Load Progress** - Nút khôi phục tiến độ +3. **Auto Save** - Tự động lưu mỗi 50 pixels và khi dừng +4. **Smart UI Restore** - Khôi phục UI đúng trạng thái trước khi dừng +5. **Progress Notification** - Thông báo khi khởi động nếu có progress đã lưu + +### Cách sử dụng: + +1. **Upload ảnh và chọn vị trí** như bình thường +2. **Bắt đầu painting** - script sẽ tự động lưu tiến độ +3. **Dừng script** khi cần - tiến độ sẽ được lưu tự động +4. **Khởi động lại script** - sẽ có thông báo nếu có tiến độ đã lưu +5. **Click "Load Progress"** để tiếp tục từ vị trí đã dừng +6. **UI sẽ hiển thị** đúng trạng thái như trước khi dừng (progress bar, pixels painted, vị trí, etc.) + +### Dữ liệu được lưu: +- Thông tin ảnh và pixels +- Vị trí hiện tại trong painting +- Số pixels đã paint +- Map các pixels đã paint (để tránh paint lại) +- Vị trí bắt đầu và region +- Màu sắc available +- Ngôn ngữ đã chọn + +### Auto Save: +- Mỗi 50 pixels được paint +- Khi user dừng script (Stop button) +- Clear progress khi hoàn thành painting + +### Thông báo: +- Tiếng Việt và English support +- Hiển thị thời gian lưu và tiến độ +- Xác nhận trước khi load