diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
new file mode 100644
index 00000000..48f7937f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -0,0 +1,100 @@
+name: Bug Report
+description: Found a bug? Report it here!
+title: "[BUG] "
+labels: ["bug"]
+assignees:
+ - Patricklumowa
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to fill out this template!
+ **The more information you provide, the easier it will be to identify and fix the bug.**
+ If you want a guide on filing bug reports effectively, you can [find it here](https://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
+ - type: checkboxes
+ id: checked-bugs
+ attributes:
+ label: Have you checked to see if this issue has already been reported?
+ options:
+ - label: This bug has not been previously reported.
+ validations:
+ required: true
+ - type: textarea
+ id: describe-bug
+ attributes:
+ label: Describe The Bug
+ description: "Explain what goes wrong when using the bot."
+ placeholder: "E.g. The bot places pixels on the wrong coordinates or skips areas."
+ validations:
+ required: true
+ - type: textarea
+ id: expected-behavior
+ attributes:
+ label: Expected Behavior
+ description: "Describe what you expected to happen or why you think the current behavior is wrong."
+ placeholder: |
+ E.g. The bot should place pixels exactly according to the template image.
+ E.g. The bot should respect rate limits and avoid bans.
+ validations:
+ required: false
+ - type: textarea
+ id: things-tried
+ attributes:
+ label: Things You Tried
+ description: "List all attempts you made to fix or work around the bug."
+ placeholder: "E.g. Restarted the bot, tried a different template, or updated the pixel map."
+ validations:
+ required: false
+ - type: textarea
+ id: reproduce-bug
+ attributes:
+ label: Reproduce The Bug
+ description: "Explain exactly how to trigger the bug."
+ placeholder: |
+ E.g.
+ 1. Launch the bot
+ 2. Load a template image
+ 3. Start pixel placement
+ 4. Observe that some pixels are placed incorrectly or skipped
+ validations:
+ required: false
+ - type: textarea
+ id: error-message
+ attributes:
+ label: Error Message
+ description: "If there are error messages, add them here."
+ placeholder: "E.g. Error: Coordinates out of range!"
+ validations:
+ required: false
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: "Include screenshots or links to illustrate the problem."
+ placeholder: "E.g. Imgur link showing the pixel placement error"
+ validations:
+ required: false
+ - type: textarea
+ id: system-information
+ attributes:
+ label: System Information
+ description: "Provide details about your OS, bot version, and browser. Please fill this out!"
+ placeholder: |
+ OS: (E.g. Windows)
+ wplace-autoBOT Version: 2.0
+ Browser: Mozilla 142.0
+ You found an easter egg :3
+ value: |
+ OS:
+ wplace-autoBOT Version:
+ Browser:
+ validations:
+ required: true
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Information
+ description: "Add any other context about the problem here."
+ placeholder: "E.g. This issue started after the last update or happens only with large templates."
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..c92e6333
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Community Support & Questions (Discord)
+ url: https://discord.gg/knkNRYyQcm
+ about: Join the Discord if you have questions or want to discuss AutoBOT with the community.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
new file mode 100644
index 00000000..8d442ebf
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -0,0 +1,48 @@
+name: Feature Request
+description: Have an idea you want added? Suggest it here!
+title: "[FEAT] "
+labels: ["enhancement"]
+assignees:
+ - Patricklumowa
+body:
+ - type: textarea
+ id: related-problem
+ attributes:
+ label: Is your feature request related to a problem? Please describe.
+ description: "Describe what the problem is."
+ placeholder: |
+ E.g. It's tedious to manually place pixels to recreate complex designs.
+ E.g. The bot currently can't detect areas that need fixing automatically.
+ validations:
+ required: false
+ - type: textarea
+ id: feature-solution
+ attributes:
+ label: Describe the solution you'd like
+ description: "Describe what you want to happen."
+ placeholder: |
+ E.g. Add an automatic pixel placement feature that matches a template image.
+ E.g. Implement prioritization of important areas first, like outlines or text.
+ E.g. Allow configuration of rate limits to avoid getting banned.
+ validations:
+ required: true
+ - type: textarea
+ id: feature-alternatives
+ attributes:
+ label: Describe alternatives you've considered
+ description: "Describe any alternative solutions or features you've considered."
+ placeholder: |
+ E.g. Manually placing pixels myself.
+ E.g. Using a different bot that doesn't support template images.
+ E.g. Semi-automatic scripts that still require manual correction.
+ validations:
+ required: false
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: Additional context
+ description: Add any other context or screenshots about the feature request here. If your feature is based on "something," please add any links relative to that "something" here.
+ placeholder: |
+ E.g. A sample template image the bot should replicate.
+ E.g. A video of the bot in action or a similar tool online.
+ E.g. Notes on color palette, priority zones, or timing restrictions.
\ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
new file mode 100644
index 00000000..5b2ef769
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
@@ -0,0 +1,28 @@
+# Pull Request
+Fill out the following details to submit your PR.
+
+## Summary
+Please briefly describe the changes in your PR.
+E.g. Fixes display bug with templates.
+E.g. Adds a template tab that users can manage all templates through.
+
+## Related Issue(s)
+Link to the related issues your PR would solve here.
+E.g. Fixes #14
+E.g. Adds #4
+
+## Changes
+Select the type of change your PR is:
+- [ ] Feature
+- [ ] Bug fix
+- [ ] Documentation
+- [ ] Refactoring
+- [ ] Build
+- [ ] Other
+
+## Checklist
+- [ ] This PR follows the project's style of coding and documentation.
+- [ ] Auto-Bot has been verified to work correctly for this PR.
+
+## Additional Notes
+Anything else reviewers should know?
\ No newline at end of file
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 00000000..8d1c7f47
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,187 @@
+name: Pages – aggregate all branches
+
+on:
+ push:
+ branches: ["**"]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: pages
+ cancel-in-progress: true
+
+jobs:
+ aggregate_deploy:
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deploy.outputs.page_url }}
+
+ steps:
+ - name: Checkout (shallow)
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Configure Pages
+ uses: actions/configure-pages@v5
+
+ - name: Aggregate and build recursive directory pages
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ # jq for JSON escaping (present on runners, but ensure)
+ if ! command -v jq >/dev/null 2>&1; then
+ sudo apt-get update -y
+ sudo apt-get install -y jq
+ fi
+
+ REPO="${GITHUB_REPOSITORY#*/}"
+ SITE_ROOT="/${REPO}/"
+
+ mkdir -p public
+ : > public/.nojekyll
+
+ # Tiny favicon assets to avoid 404s
+ printf "%s" \
+ "" \
+ > public/favicon.svg
+
+ # List remote branches
+ mapfile -t BRANCHES < <(
+ git ls-remote --heads origin | awk '{print $2}' \
+ | sed 's@refs/heads/@@'
+ )
+
+ # Landing page (no globals except window.__branches)
+ cat > public/index.html <<'HTML'
+
+
+
+
+
Branches
+
+
Branches
+
+
+
+ HTML
+
+ # Emit index.html for a directory
+ gen_index_dir() {
+ local DIR="$1" ROOT="$2" BR="$3"
+ local rel="${DIR#$ROOT}"
+ local title="Branch: ${BR}${rel:-/}"
+
+ {
+ echo ""
+ echo ""
+ echo ""
+ echo "${title}"
+ echo ""
+ echo "
+Dieses Projekt ist ein Fork von https://github.com/DarkModde/WPlace-AutoBOT
+Tritt unserem Discord bei: https://discord.gg/CBB4abRmGM
+
+
+Praktische Scripts mit intuitiven Menüs, die dein Leben auf WPlace erleichtern!
+Perfekt für alle, die automatisch Level aufsteigen oder riesige Pixel Artworks erstellen wollen ohne Zeit zu verschwenden.
+
+
+
+
+
+⚠️ Hinweis: Dieses Script dient ausschließlich zu Bildungszwecken, um zu demonstrieren, was auf wplace.live möglich ist. Wir übernehmen keine Verantwortung für die Nutzung.
+
+
+
+Wenn du dich fragst — ja! Diese Scripts wurden von einem Team aus Brasilianern, Vietnamesen und Indonesiern entwickelt. HUEHUE!
+
+
+---
+
+
🚀┃Wie man die Scripts benutzt:
+
+
+Es ist super einfach: Kopiere einen der Codes unten, füge ihn in die Lesezeichenleiste deines Browsers ein,
+und führe ihn aus, während du auf wplace.live bist.
+
+
+### 🎯┃Auto-Farm
+**Auto-Farm verwendet Ladungen, um Levels zu Farmen. Es zeichnet keine Bilder! Nutze dafür Auto-Image.**
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval);
+````
+
+### 🖼️┃Auto-Image
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval);
+```
+
+
+
📖┃Tutorials
+
+---
+
+
+
+---
+
+
+
+---
+
+
+
+
+
+---
+
+### ✨┃Features
+
+* Automatisches Farmen von Drops/Levels
+* Korrektes Überspringen von Pixeln mit falscher Farbe
+* Overlay (BlueMarble nicht mehr nötig)
+* Malen über mehrere Tiles
+* AutoCaptcha-Solver mit Turnstile Token Generator
+* Erweiterte Farbverarbeitung
+* Mehrsprachige Unterstützung
+* Multi-Account über Speicher- und Ladefunktion
+* Funktioniert auch auf Mobilgeräten \:P
+
+---
+
+### 📋┃To-Do
+
+* [x] AutoFarm zum Laufen bringen
+* [ ] Beide Scripts zu einem zusammenführen
+* [ ] Multi-Account-Switcher (in Entwicklung)
+* [ ] Multi-Account-Warteschlange
+* [ ] Overlay-System hinzufügen
+* [ ] Unterstützung über mehrere Tiles
+* [ ] AutoCaptcha-Solver hinzufügen
+* [ ] Korrektes Überspringen von Pixeln
+* [ ] Separate CSS-Dateien
+
+---
+
+
+
+
+
+
+
+
diff --git a/NL.md b/NL.md
new file mode 100644
index 00000000..9b3827eb
--- /dev/null
+++ b/NL.md
@@ -0,0 +1,124 @@
+
+
+
+
+
WPlace AutoBOT
+
+Dit project is een fork van https://github.com/DarkModde/WPlace-AutoBOT
+
+
+ Overweeg om onze Discord te bekijken: https://discord.gg/knkNRYyQcm
+
+
+ Praktische scripts met intuïtieve menu’s om je leven makkelijker te maken op WPlace!
+ Perfect voor wie automatisch wil levelen of enorme pixelkunst wil maken — zonder tijd te verspillen.
+
+ Waarschuwing: Dit is een puur educatief script om te laten zien wat er op wplace.live mogelijk is, wij zijn niet verantwoordelijk voor wat er gebeurt als je het script gebruikt.
+
+
+
+
+
+ Als je je afvraagt—ja! Deze scripts zijn ontwikkeld door een team bestaande uit Brazilianen, Vietnamezen en Indonesiërs. HUEHUE!
+
+
+
+
+
+
+
+
+
+---
+
+
🚀┃Hoe de scripts te gebruiken:
+
+
+ Het is super eenvoudig: kopieer een van de codes hieronder, plak deze in de bladwijzerbalk van je browser,
+ en voer het uit terwijl je op wplace.live bent.
+
+
+
+
+### 🎯┃Auto-Farm
+#### AUTOFARM GEBRUIKT LADINGEN OM LEVELS TE KRIJGEN, HET TEKENT GEEN AFBEELDING VOOR JE. GEBRUIK DAARVOOR AUTO-IMAGE
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval);
+```
+
+### 🖼️┃Auto-Image
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval);
+```
+
+
+
+```
diff --git a/README-ES.md b/README-ES.md
new file mode 100644
index 00000000..df1e3712
--- /dev/null
+++ b/README-ES.md
@@ -0,0 +1,125 @@
+
+
+
+
+
WPlace AutoBOT
+
+Este proyecto es un fork de https://github.com/DarkModde/WPlace-AutoBOT
+
+
+ Considera unirte a nuestro Discord: https://discord.gg/knkNRYyQcm
+
+
+ ¡Scripts prácticos con menús intuitivos para facilitarte la vida en WPlace!
+ Perfecto para quienes quieren subir de nivel automáticamente o construir pixel arts masivos, sin perder tiempo.
+
+ ⚠️ Advertencia: Este es un script puramente educativo para demostrar lo que se puede hacer en wplace.live. No nos hacemos responsables de lo que suceda si utilizas el script.
+
+
+
+
+
+ Por si te lo preguntas, ¡sí! Estos scripts fueron desarrollados por un equipo de brasileños, vietnamitas e indonesios. ¡HUEHUE!
+
+
+
+
+
+
+
+
+
+---
+
+
🚀┃Cómo usar los scripts:
+
+
+ Es súper fácil: copia uno de los códigos de abajo, pégalo en la barra de marcadores de tu navegador
+ y ejecútalo mientras estás en wplace.live.
+
+
+
+
+### 🎯┃Auto-Farm
+#### EL AUTOFARM USA CARGAS PARA OBTENER NIVELES, NO DIBUJA UNA IMAGEN POR TI. POR FAVOR, USA AUTO-IMAGEN PARA ESO.
+```js
+javascript:fetch("[https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js](https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js)").then(t=>t.text()).then(eval);
+```
+
+### 🖼️┃Auto-Imagen
+
+```js
+javascript:fetch("[https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js](https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js)").then(t=>t.text()).then(eval);
+```
+
+
+
1. Ninguno de los scripts funcionará si cierras la pestaña del navegador. Debes mantener la pestaña de WPlace abierta, incluso si está en segundo plano.
+>
2. Nunca ejecutes ambos scripts en la misma página, esto puede bugear tu WPlace. Si sucede, ¡simplemente recarga la página!
+>
3. Si el script no detecta los colores disponibles, simplemente haz clic en el botón "Pintar" para mostrarlos y solo después inicia el Auto-Imager.
+
+
+
+### ✨┃Características
+
+- [x] Farmea automáticamente gotas/niveles
+- [x] Omite píxeles del color correcto
+- [x] Superposición (ya no necesitas Bluemarble)
+- [x] Pinta sobre múltiples áreas (tiles)
+- [x] Cambiador automático de multicuentas a través de otra rama: https://github.com/Wplace-AutoBot/WPlace-AutoBOT/tree/Acc-switch (Eficiente en RAM LOL)
+- [x] Solucionador de AutoCaptcha con generador de tokens Turnstile
+- [x] Procesamiento de color avanzado
+- [x] Soporte multi-idioma
+- [x] Multicuentas a través de la función de guardar y cargar
+- [x] Funciona en móviles :P
+
+
+---
+
+
+---
+
+
+
+
+
+---
+
+### 📋┃Tareas pendientes
+
+- [x] Hacer que el AutoFarm funcione
+- [ ] Unir ambos scripts en uno solo
+- [x] Añadir sistema de superposición
+- [x] Añadir soporte para pintar entre áreas (Cross Tiles)
+- [x] Añadir solucionador de AutoCaptcha
+- [x] Añadir omisión de píxeles del color correcto
+- [x] Separar CSS
+
+---
+
+- Contribuidores:
+
+
+
+
This project was fork from https://github.com/DarkModde/WPlace-AutoBOT
@@ -15,8 +19,14 @@ This project was fork from https://github.com/DarkModde/WPlace-AutoBOT
- It wasn't me who "hacked" WPlace and placed that giant Herobrine there...
- But if you're wondering: yes! These scripts were made by a Brazilian and VietNamese. HUEHUE!
+ If you're wondering—yes! These scripts were Developed by a team consisting of Brazilians, Vietnamese, and Indonesians. HUEHUE!
+
+
+
+
+
+
+
---
@@ -33,13 +43,13 @@ This project was fork from https://github.com/DarkModde/WPlace-AutoBOT
### 🎯┃Auto-Farm
#### AUTOFARM USES CHARGES TO GET LEVELS, IT DOES NOT DRAW AN IMAGE FOR YOU. PLEASE USE AUTO-IMAGE FOR THAT
```js
-javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval);
+javascript:(async()=>{ const u='https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js'; const c=await (await fetch(u,{cache:'no-store'})).text(); const blob=new Blob([c],{type:'application/javascript'}); const url=URL.createObjectURL(blob); const s=document.createElement('script'); s.src=url; document.body.appendChild(s); })();
```
### 🖼️┃Auto-Image
```js
-javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval);
+javascript:(async()=>{ const u='https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js'; const c=await (await fetch(u,{cache:'no-store'})).text(); const blob=new Blob([c],{type:'application/javascript'}); const url=URL.createObjectURL(blob); const s=document.createElement('script'); s.src=url; document.body.appendChild(s); })();;
```
@@ -69,24 +79,52 @@ javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBO
+### ✨┃Features
+
+- [x] Automatically farms droplets/levels
+- [x] Correct Color Pixel Skip
+- [x] Overlay(you dont need bluemarble anymore)
+- [x] Paint over multiple tiles
+- [x] Multi Account Auto Switcher Via Other Branch ; https://github.com/Wplace-AutoBot/WPlace-AutoBOT/tree/Acc-switch (Ram Efficient LOL)
+- [x] AutoCaptcha Solver With Turnstile Token Generator
+- [x] Wider Paint Control Options
+- [x] Advance Color Processing
+- [x] MultiLanguage Support
+- [x] Multi account through save and load feature
+- [x] Works on mobile :P
+
+
+---
+
+
---
-
+
+---
+
+
+
---
### 📋┃To-do
- [x] Making AutoFarm working
-- [ ] Merge both scripts into one
-- [x] Fix script bugs
-- [] Add new options
-- [ ] Tutorials
+- [ ] Merge both scripts into one
+- [x] Add Overlay system
+- [x] Add Cross Tiles Support
+- [x] Add AutoCaptcha Solver
+- [x] Add Correct Color Pixel Skip
+- [x] Separate Css
---
+- Contributors:
+
+
+
diff --git a/RU.md b/RU.md
new file mode 100644
index 00000000..cb07c8e1
--- /dev/null
+++ b/RU.md
@@ -0,0 +1,126 @@
+
+
+
+
+
WPlace AutoBOT
+
+Этот проект является форком от https://github.com/DarkModde/WPlace-AutoBOT
+
+
+ Взгляните на наш дискорд: https://discord.gg/knkNRYyQcm
+
+
+ Практичные скрипты с интуитивно понятным меню облегчат вам жизнь на WPlace!
+ Идеально подходит для тех, кто хочет автоматически повышать свой уровень или создавать масштабные пиксельные рисунки, не теряя времени даром.
+
+ Внимание: Это чисто образовательный скрипт, демонстрирующий, что можно сделать на wplace.live. Мы не несем ответственности за то, что произойдет, если вы воспользуетесь этим скриптом.
+
+
+
+
+
+ Если вам интересно — да! Эти сценарии были разработаны командой, состоящей из бразильцев, вьетнамцев и индонезийцев. HUEHUE!
+
+
+
+
+
+
+
+
+
+---
+
+
🚀┃Как пользоваться скриптами:
+
+
+ Это очень просто: скопируйте один из приведенных ниже кодов, вставьте его в строку закладок вашего браузера,
+ и запустите его, находясь на wplace.live.
+
+
+
+
+### 🎯┃Авто-Фарм
+#### АВТОФАРМ ИСПОЛЬЗУЕТ ЗАРЯДЫ ДЛЯ ПОЛУЧЕНИЯ УРОВНЕЙ, А НЕ РИСУЕТ ИЗОБРАЖЕНИЕ ЗА ВАС. ПОЖАЛУЙСТА, ИСПОЛЬЗУЙТЕ ДЛЯ ЭТОГО АВТОМАТИЧЕСКОЕ ИЗОБРАЖЕНИЕ
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval);
+```
+
+### 🖼️┃Auto-Image
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval);
+```
+
+
+
1. Ни один из сценариев не будет работать, если вы закроете вкладку браузера. Вы должны оставить вкладку WPlace открытой, даже если она находится в фоновом режиме.
+>
2. Никогда не запускайте оба скрипта на одной странице — это может привести к ошибке в вашем WPlace. Если это произойдет, просто обновите страницу!
+>
3. Если скрипт не распознает доступные цвета, просто нажмите кнопку "Нарисовать", чтобы отобразить их — только после этого запустите автоматическое создание изображений.
+
+
+
+### ✨┃Функции
+
+- [x] Автоматически фармит заряды/уровни
+- [x] Пропуск пикселей с правильным цветом
+- [x] Оверлей(вам больше не нужен bluemarble)
+- [x] Нанесение краски на несколько плиток
+- [x] Автокапча с генератором токенов Turnstile
+- [x] Предварительная обработка цвета
+- [x] Мультиязычность
+- [x] Несколько учетных записей с помощью функции сохранения и загрузки
+
+
+---
+
+
+---
+
+
+
+
+
+---
+
+### 📋┃To-do
+
+- [x] Как заставить работать автоферму
+- [ ] Объединить оба скрипта в один
+- [ ] Переключатель нескольких учетных записей (в настоящее время разрабатывается)
+- [ ] Система очередей для нескольких учетных записей
+- [x] Добавить систему наложения
+- [x] Добавить поддержку перекрестных плиток
+- [x] Добавить средство автоподчеты
+- [x] Добавить правильный пропуск цветного пикселя
+- [ ] Отдельный Css
+- [ ] Учебные пособия
+
+---
+
+- Контрибьютуоры:
+
+
+
+
+
+
diff --git a/UK.md b/UK.md
new file mode 100644
index 00000000..4703d2ed
--- /dev/null
+++ b/UK.md
@@ -0,0 +1,125 @@
+
+
+
+
+
WPlace AutoBOT
+
+Цей проєкт є форком з https://github.com/DarkModde/WPlace-AutoBOT
+
+
+ Завітайте на наш Discord: https://discord.gg/knkNRYyQcm
+
+
+ Практичні скрипти з інтуїтивним меню, щоб зробити ваше життя на WPlace простішим!
+ Ідеально для тих, хто хоче автоматично прокачувати рівень або будувати масивні піксель-арти — без витрати часу.
+
+ ⚠️ Увага: Це освітній скрипт, створений для демонстрації можливостей на wplace.live. Ми не несемо відповідальності за наслідки його використання.
+
+
+
+
+
+ Якщо ви цікавитеся — так! Скрипти були розроблені командою з Бразилії, В’єтнаму та Індонезії. HUEHUE!
+
+
+
+
+
+
+
+
+
+---
+
+
🚀┃Як користуватися скриптами:
+
+
+ Це дуже просто: скопіюйте один з кодів нижче, вставте його у панель закладок вашого браузера,
+ та запустіть, коли перебуваєте на wplace.live.
+
+
+
+
+### 🎯┃Авто-Фарм
+#### AUTOFARM ВИКОРИСТОВУЄ ЧАРДЖІ ДЛЯ ОТРИМАННЯ РІВНІВ, ВІН НЕ МАЛЮЄ ЗОБРАЖЕННЯ. ДЛЯ ЦЬОГО ВИКОРИСТОВУЙТЕ AUTO-IMAGE
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval);
+```
+
+### 🖼️┃Авто-Зображення
+
+```js
+javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval);
+```
+
+
+
1. Жоден скрипт не працюватиме, якщо ви закриєте вкладку браузера. Вкладка WPlace має залишатися відкритою, навіть у фоні.
+>
2. Ніколи не запускайте обидва скрипти на одній сторінці — це може зламати WPlace. Якщо це сталося — просто оновіть сторінку!
+>
3. Якщо скрипт не визначає доступні кольори, натисніть кнопку «Paint», щоб вони з’явилися — лише тоді запускайте Auto-Imager.
+
+
+
+### ✨┃Можливості
+
+- [x] Автоматичне фармлення крапель/рівнів
+- [x] Пропуск пікселів з правильним кольором
+- [x] Оверлей (більше не потрібен bluemarble)
+- [x] Малювання по кількох тайлах
+- [x] Авто-розв’язувач капчі з генератором токенів Turnstile
+- [x] Розширена обробка кольорів
+- [x] Підтримка кількох мов
+- [x] Багатоакаунтність через функції збереження та завантаження
+- [x] Працює на мобільних :P
+
+
+---
+
+---
+
+
+
+
+
+---
+
+### 📋┃Список завдань
+
+- [x] Запуск AutoFarm
+- [ ] Об’єднання обох скриптів в один
+- [ ] Перемикач акаунтів (у розробці)
+- [ ] Система черги акаунтів
+- [x] Додати систему оверлею
+- [x] Додати підтримку крос-тайлів
+- [x] Додати авто-розв’язувач капчі
+- [x] Додати пропуск правильних пікселів
+- [ ] Винести CSS окремо
+
+---
+
+- Контриб’ютори:
+
+
+
+
+
+
diff --git a/auto-image-styles.css b/auto-image-styles.css
new file mode 100644
index 00000000..95b1ffa8
--- /dev/null
+++ b/auto-image-styles.css
@@ -0,0 +1,1914 @@
+/* WPlace Auto-Image Bot - Unified CSS Styles (decoupled from JS)
+ Why: bring external CSS in-sync with the UI that Auto-Image.js renders,
+ fix layout (positions, widths, z-index), and ensure class names match JS
+ (e.g., .wplace-dragging) so buttons and panels behave correctly. */
+
+/* ========================= */
+/* Theme tokens (CSS vars) */
+/* ========================= */
+
+/* Import theme files - classic is default */
+@import url('https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/classic.css');
+@import url('https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/classic-light.css');
+@import url('https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/neon.css');
+
+/* Default :root CSS variables for 100% classic theme compliance */
+/* These ensure the bot works perfectly even if theme files fail to load */
+:root {
+ /* Classic theme colors - exact upstream main values */
+ --wplace-primary: linear-gradient(135deg, #000 0%, #1a1a1a 100%);
+ --wplace-secondary: linear-gradient(135deg, #111 0%, #2a2a2a 100%);
+ --wplace-accent: #222;
+ --wplace-text: #fff;
+ --wplace-highlight: #775ce3;
+ --wplace-highlight-secondary: #d3a4ff;
+ --wplace-success: #0f0;
+ --wplace-error: #f00;
+ --wplace-warning: #fa0;
+
+ /* UI properties */
+ --wplace-radius: 12px;
+ --wplace-btn-radius: 16px;
+ --wplace-border-style: solid;
+ --wplace-border-width: 1px;
+ --wplace-border-color: #222;
+ --wplace-shadow: 0 8px 32px rgb(0 0 0 / 60%), 0 0 0 1px rgb(255 255 255 / 10%);
+ --wplace-backdrop: blur(10px);
+ --wplace-font: 'Segoe UI', roboto, sans-serif;
+
+ /* Z-index layers */
+ --wplace-z-overlay: 10000;
+ --wplace-z-alert: 10002;
+ --wplace-z-settings: 10002;
+
+ /* Feature toggles */
+ --wplace-scanline: 0;
+ --wplace-pixel-blink: 0;
+
+ /* Icon colors */
+ --wplace-icon-primary: #4facfe;
+ --wplace-icon-secondary: #00f2fe;
+ --wplace-icon-palette: #f093fb;
+
+ /* Additional UI colors */
+ --wplace-danger: #ff6a6a;
+ --wplace-danger-dark: #ff4757;
+ --wplace-muted-text: #fffb;
+
+ /* Text variants */
+ --wplace-text-secondary: rgb(255 255 255 / 90%);
+ --wplace-text-muted: rgb(255 255 255 / 70%);
+ --wplace-text-dim: rgb(255 255 255 / 60%);
+ --wplace-text-faded: rgb(255 255 255 / 80%);
+
+ /* Background variants */
+ --wplace-bg-input: rgb(255 255 255 / 15%);
+ --wplace-bg-subtle: rgb(255 255 255 / 10%);
+ --wplace-bg-faint: rgb(255 255 255 / 8%);
+ --wplace-bg-ghost: rgb(255 255 255 / 6%);
+ --wplace-bg-whisper: rgb(255 255 255 / 5%);
+
+ /* Border variants */
+ --wplace-border-subtle: rgb(255 255 255 / 20%);
+ --wplace-border-faint: rgb(255 255 255 / 15%);
+ --wplace-border-ghost: rgb(255 255 255 / 10%);
+ --wplace-border-ultra-faint: rgb(255 255 255 / 5%);
+
+ /* Shadow variants */
+ --wplace-shadow-drag: 0 12px 40px rgb(0 0 0 / 80%), 0 0 0 2px rgb(255 255 255 / 20%);
+ --wplace-shadow-notification: 0 4px 12px rgb(0 0 0 / 30%);
+ --wplace-shadow-slider-thumb: 0 3px 6px rgb(0 0 0 / 30%), 0 0 0 2px var(--wplace-icon-primary);
+ --wplace-shadow-slider-hover: 0 4px 8px rgb(0 0 0 / 40%), 0 0 0 3px var(--wplace-icon-primary);
+
+ /* Animation colors */
+ --wplace-pulse-start: rgb(0 255 0 / 70%);
+ --wplace-pulse-mid: rgb(0 255 0 / 0%);
+ --wplace-pulse-end: rgb(0 255 0 / 0%);
+
+ /* Slider colors - defaults for classic compatibility */
+ --wplace-slider-thumb-bg: white;
+ --wplace-slider-track-bg: linear-gradient(
+ to right,
+ var(--wplace-icon-primary) 0%,
+ var(--wplace-icon-secondary) 100%
+ );
+}
+
+/* Theme classes are now defined in separate files */
+/* Classic theme: ./themes/classic.css */
+/* Neon theme: ./themes/neon.css */
+
+/* ========================= */
+/* Core animations (shared) */
+/* ========================= */
+@keyframes neon-glow {
+ 0%,
+ 100% {
+ text-shadow:
+ 0 0 5px currentcolor,
+ 0 0 10px currentcolor,
+ 0 0 15px currentcolor;
+ }
+
+ 50% {
+ text-shadow:
+ 0 0 2px currentcolor,
+ 0 0 5px currentcolor,
+ 0 0 8px currentcolor;
+ }
+}
+
+@keyframes pixel-blink {
+ 0%,
+ 50% {
+ opacity: 1;
+ }
+
+ 51%,
+ 100% {
+ opacity: 0.7;
+ }
+}
+
+@keyframes scanline {
+ 0% {
+ transform: translateY(-100%);
+ }
+
+ 100% {
+ transform: translateY(400px);
+ }
+}
+
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 var(--wplace-pulse-start);
+ }
+
+ 70% {
+ box-shadow: 0 0 0 10px var(--wplace-pulse-mid);
+ }
+
+ 100% {
+ box-shadow: 0 0 0 0 var(--wplace-pulse-end);
+ }
+}
+
+@keyframes slide-in {
+ from {
+ transform: translateY(-10px);
+ opacity: 0;
+ }
+
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+@keyframes slide-down {
+ from {
+ transform: translateX(-50%) translateY(-20px);
+ opacity: 0;
+ }
+
+ to {
+ transform: translateX(-50%) translateY(0);
+ opacity: 1;
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ transform: translateX(-100%);
+ }
+
+ 100% {
+ transform: translateX(100%);
+ }
+}
+
+/* ========================= */
+/* Main containers (fixed) */
+/* Align with JS structure */
+/* ========================= */
+#wplace-image-bot-container {
+ position: fixed;
+ top: 20px;
+ left: 20px;
+ width: 280px;
+ max-height: calc(100vh - 40px);
+ padding: 0;
+ z-index: 9998;
+ animation: slide-in 0.4s ease-out;
+ overflow: hidden auto;
+ transition: all 0.3s ease;
+ user-select: none;
+
+ /* Default classic theme styling for 100% compliance */
+ background: var(--wplace-primary);
+ color: var(--wplace-text);
+ border-radius: var(--wplace-radius);
+ box-shadow: var(--wplace-shadow);
+ font-family: var(--wplace-font);
+ backdrop-filter: var(--wplace-backdrop);
+ border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
+}
+
+#wplace-image-bot-container.wplace-dragging {
+ transition: none;
+ box-shadow: var(--wplace-shadow-drag);
+ transform: scale(1.02);
+ z-index: 9999;
+}
+
+#wplace-image-bot-container.wplace-compact {
+ width: 240px;
+}
+
+#wplace-image-bot-container.wplace-minimized {
+ width: 200px;
+ height: auto;
+ overflow: hidden;
+}
+
+/* Stats container sits to the right of main (280 + 30 = 330) */
+#wplace-stats-container {
+ position: fixed;
+ top: 20px;
+ left: 310px;
+ width: 230px;
+ max-height: calc(100vh - 40px);
+ padding: 0;
+ z-index: 9997;
+ animation: slide-in 0.4s ease-out;
+ overflow-y: auto;
+ transition: all 0.3s ease;
+ user-select: none;
+ display: none;
+
+ /* Default classic theme styling for 100% compliance */
+ background: var(--wplace-primary);
+ color: var(--wplace-text);
+ border-radius: var(--wplace-radius);
+ box-shadow: var(--wplace-shadow);
+ font-family: var(--wplace-font);
+ backdrop-filter: var(--wplace-backdrop);
+ border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
+}
+
+#wplace-stats-container.wplace-dragging {
+ transition: none;
+}
+
+/* Back-compat for legacy class (some earlier CSS used this) */
+.wplace-drag-active {
+ transition: none !important;
+ box-shadow: var(--wplace-shadow-drag) !important;
+ transform: scale(1.02) !important;
+ z-index: 9999 !important;
+}
+
+/* ========================= */
+/* Header and content blocks */
+/* ========================= */
+.wplace-header {
+ padding: 8px 12px;
+ font-size: 13px;
+ font-weight: 700;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: move;
+ user-select: none;
+ border-bottom: 1px solid var(--wplace-border-ghost);
+ transition: background 0.2s ease;
+ position: relative;
+ z-index: 2;
+
+ /* Default styling for 100% classic compliance */
+ background: var(--wplace-secondary);
+ color: var(--wplace-highlight);
+ text-shadow: 0 1px 2px rgb(0 0 0 / 50%);
+}
+
+.wplace-header-title {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.wplace-header-controls {
+ display: flex;
+ gap: 6px;
+}
+
+.wplace-header-btn {
+ border: none;
+ cursor: pointer;
+ width: 18px;
+ height: 18px;
+ padding: 0;
+ font-size: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s;
+
+ /* Default styling for 100% classic compliance */
+ background: rgb(255 255 255 / 10%);
+ color: var(--wplace-text);
+ border-radius: 4px;
+ font-family: var(--wplace-font);
+}
+
+.wplace-header-btn:hover {
+ transform: scale(1.1);
+
+ /* Default styling for 100% classic compliance */
+ background: var(--wplace-accent);
+ color: var(--wplace-text);
+}
+
+.wplace-content {
+ display: block;
+ position: relative;
+ z-index: 2;
+ padding: 12px;
+}
+
+.wplace-content.wplace-hidden {
+ display: none;
+}
+
+/* Sections */
+.wplace-status-section {
+ margin-bottom: 6px;
+ padding: 8px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-section {
+ margin-bottom: 6px;
+ padding: 8px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-section-title {
+ font-size: 11px;
+ font-weight: 600;
+ margin-bottom: 8px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: pointer;
+ gap: 6px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-section-title i.arrow {
+ transition: transform 0.3s ease;
+}
+
+.wplace-section.collapsed .wplace-section-title i.arrow {
+ transform: rotate(-90deg);
+}
+
+/* ========================= */
+/* Controls and buttons */
+/* ========================= */
+.wplace-controls {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.wplace-cooldown-control {
+ margin-top: 8px;
+}
+
+.wplace-section.collapsed .wplace-controls,
+.wplace-section.collapsed .wplace-cooldown-control {
+ max-height: 0;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.wplace-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 8px;
+}
+
+.wplace-row.single {
+ grid-template-columns: 1fr;
+}
+
+.wplace-btn {
+ padding: 8px 12px;
+ border: none;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ font-size: 11px;
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ font-weight: 500;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none !important;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-btn::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ transition: left 0.5s ease;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-btn:disabled::before {
+ display: none;
+}
+
+.wplace-btn:hover:not(:disabled)::before {
+ left: 100%;
+}
+
+.wplace-btn:hover:not(:disabled) {
+ transform: translateY(-1px);
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-btn:active:not(:disabled) {
+ transform: translateY(0);
+}
+
+/* Button variants moved to theme files */
+
+/* ========================= */
+/* Stats and progress */
+/* ========================= */
+.wplace-stats {
+ margin-bottom: 8px;
+ padding: 8px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-stat-item {
+ display: flex;
+ justify-content: space-between;
+ padding: 4px 0;
+ font-size: 11px;
+ border-bottom: 1px solid var(--wplace-border-ultra-faint);
+}
+
+.wplace-stat-item:last-child {
+ border-bottom: none;
+}
+
+.wplace-stat-label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ opacity: 0.9;
+ font-size: 10px;
+}
+
+.wplace-stat-value {
+ font-weight: 600;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-colors-section {
+ margin-top: 10px;
+ padding-top: 8px;
+ border-top: 1px solid var(--wplace-border-ultra-faint);
+}
+
+.wplace-stat-colors-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(16px, 1fr));
+ gap: 4px;
+ margin-top: 8px;
+ padding: 4px;
+ max-height: 80px;
+ overflow-y: auto;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-colors-placeholder {
+ text-align: center;
+ color: #888;
+ padding: 20px;
+ font-style: italic;
+}
+
+.wplace-cooldown-value {
+ font-weight: bold;
+ min-width: 20px;
+ text-align: center;
+ display: inline-block;
+}
+
+.wplace-stat-color-swatch {
+ width: 16px;
+ height: 16px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Progress */
+.wplace-progress {
+ width: 100%;
+ margin: 8px 0;
+ overflow: hidden;
+ height: 6px;
+ position: relative;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-progress-bar {
+ height: 6px;
+ transition: width 0.5s ease;
+ position: relative;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-progress-bar::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ animation: shimmer 2s infinite;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* ========================= */
+/* Status blocks */
+/* ========================= */
+.wplace-status {
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+ padding: 6px;
+ border: 1px solid;
+ font-size: 11px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Status styling moved to theme files */
+
+/* ========================= */
+/* Resize dialog */
+/* ========================= */
+.resize-container {
+ display: none;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ padding: 20px;
+ z-index: 10000;
+ width: 90%;
+ max-width: 700px;
+ max-height: 90%;
+ overflow: auto;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.resize-preview-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 15px 0;
+ height: 300px;
+ overflow: hidden;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.resize-canvas-stack {
+ position: relative;
+ transform-origin: center center;
+ display: inline-block;
+}
+
+.resize-base-canvas,
+.resize-mask-canvas {
+ position: absolute;
+ left: 0;
+ top: 0;
+ image-rendering: -moz-crisp-edges;
+ image-rendering: pixelated;
+}
+
+.resize-mask-canvas {
+ pointer-events: auto;
+}
+
+.resize-tools {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ margin-top: 8px;
+ font-size: 12px;
+}
+
+/* Missing button hover styles */
+.resize-tools button {
+ padding: 6px 10px;
+ border-radius: 6px;
+ border: 1px solid var(--wplace-border-subtle);
+ background: var(--wplace-bg-ghost);
+ color: var(--wplace-text);
+ cursor: pointer;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Button active states moved to theme files */
+
+.resize-controls {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 15px;
+ align-items: center;
+}
+
+.resize-slider {
+ width: 100%;
+ height: 4px;
+ border: none;
+ outline: none;
+ -webkit-appearance: none;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.resize-zoom-controls {
+ grid-column: 1 / -1;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 15px;
+ flex-wrap: wrap;
+}
+
+.resize-buttons {
+ display: flex;
+ gap: 10px;
+ justify-content: center;
+ margin-top: 20px;
+}
+
+
+/* ========================= */
+/* Color grid */
+/* ========================= */
+.wplace-color-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
+ gap: 10px;
+ padding-top: 8px;
+ max-height: 300px;
+ overflow-y: auto;
+}
+
+.wplace-color-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+}
+
+.wplace-color-item-name {
+ font-size: 9px;
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-swatch {
+ width: 22px;
+ height: 22px;
+ cursor: pointer;
+ transition:
+ transform 0.1s ease,
+ box-shadow 0.2s ease;
+ position: relative;
+ margin: 0 auto;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-swatch.unavailable {
+ border-style: dashed;
+ cursor: not-allowed;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-swatch:hover {
+ transform: scale(1.1);
+ z-index: 1;
+}
+
+.wplace-color-swatch:not(.active) {
+ opacity: 0.3;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-swatch.unavailable:not(.active) {
+ opacity: 0.2;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-swatch.active::after {
+ content: '✔';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: var(--wplace-text);
+ font-size: 12px;
+ font-weight: bold;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-color-divider {
+ border: none;
+ height: 1px;
+ margin: 8px 0;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* ========================= */
+/* Cooldown controls */
+/* ========================= */
+.wplace-cooldown-control label {
+ font-size: 11px;
+ margin-bottom: 4px;
+ display: block;
+}
+
+.wplace-slider-container {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.wplace-slider {
+ flex: 1;
+ -webkit-appearance: none;
+ appearance: none;
+ height: 4px;
+ outline: none;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 14px;
+ height: 14px;
+ cursor: pointer;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* ========================= */
+/* Settings container (base) */
+/* ========================= */
+#wplace-settings-container {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ padding: 0;
+ z-index: 10002;
+ display: none;
+ min-width: 420px;
+ max-width: 480px;
+ overflow: hidden;
+
+ /* Default classic theme styling for 100% compliance */
+ background: var(--wplace-primary);
+ color: var(--wplace-text);
+ border-radius: var(--wplace-radius);
+ box-shadow: var(--wplace-shadow);
+ font-family: var(--wplace-font);
+ backdrop-filter: var(--wplace-backdrop);
+ border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
+}
+
+#wplace-settings-container.show {
+ display: block;
+ animation: settings-slide-in 0.4s ease-out;
+}
+
+@keyframes settings-slide-in {
+ from {
+ opacity: 0;
+ transform: translate(-50%, -50%) scale(0.9);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate(-50%, -50%) scale(1);
+ }
+}
+
+@keyframes settings-fade-out {
+ from {
+ opacity: 1;
+ transform: translate(-50%, -50%) scale(1);
+ }
+
+ to {
+ opacity: 0;
+ transform: translate(-50%, -50%) scale(0.9);
+ }
+}
+
+.wplace-settings {
+ padding: 16px;
+ max-height: 400px;
+ overflow-y: auto;
+}
+
+.wplace-setting-section {
+ margin-bottom: 20px;
+ padding: 12px;
+}
+
+/* ========================= */
+/* Form controls */
+/* ========================= */
+.wplace-select {
+ width: 100%;
+ padding: 8px 12px;
+ font-size: 14px;
+ margin-bottom: 10px;
+}
+
+.wplace-select:focus {
+ outline: none;
+}
+
+.wplace-description {
+ font-size: 12px;
+ opacity: 0.8;
+ line-height: 1.4;
+}
+
+/* Speed controls */
+.wplace-speed-control {
+ margin-top: 12px;
+ padding: 12px;
+}
+
+.wplace-speed-label {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ font-size: 13px;
+ font-weight: 600;
+}
+
+.wplace-speed-label i {
+ margin-right: 6px;
+}
+
+
+.wplace-speed-slider {
+ flex: 1;
+ height: 8px;
+ outline: none;
+ -webkit-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ background: var(--wplace-slider-track-bg);
+ border-radius: 4px;
+}
+
+
+.wplace-speed-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-speed-slider::-moz-range-thumb {
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-speed-display {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ min-width: 90px;
+ justify-content: flex-end;
+}
+
+/* ========================= */
+/* Turnstile overlay */
+/* ========================= */
+/* Hidden invisible widget host (token generator) */
+.wplace-turnstile-hidden {
+ position: fixed !important;
+ left: -99999px !important;
+ top: -99999px !important;
+ width: 1px !important;
+ height: 1px !important;
+ pointer-events: none !important;
+ opacity: 0 !important;
+ visibility: hidden !important;
+ z-index: -99999 !important;
+ overflow: hidden !important;
+}
+
+/* Visible overlay (interactive fallback) */
+.wplace-turnstile-overlay {
+ position: fixed !important;
+ bottom: 20px !important;
+ right: 20px !important;
+ z-index: 99999 !important;
+ padding: 20px !important;
+ min-width: 300px !important;
+ max-width: 400px !important;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-turnstile-title {
+ font:
+ 600 12px/1.3 'Segoe UI',
+ sans-serif !important;
+ margin-bottom: 8px !important;
+ opacity: 0.9 !important;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-turnstile-host {
+ width: 100% !important;
+ min-height: 70px !important;
+}
+
+.wplace-turnstile-hide-btn {
+ position: absolute !important;
+ top: 6px !important;
+ right: 6px !important;
+ font-size: 11px !important;
+ background: transparent !important;
+ padding: 2px 6px !important;
+ cursor: pointer !important;
+ transition: background 0.2s ease !important;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-turnstile-hide-btn:hover {
+ /* Theme-specific styling applied via theme files */
+}
+
+/* ========================= */
+/* Alert system (used by JS) */
+/* ========================= */
+.wplace-alert-base {
+ position: fixed;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ padding: 12px 20px;
+ color: var(--wplace-text);
+ font-weight: 600;
+ z-index: 10002;
+ max-width: 400px;
+ text-align: center;
+ animation: slide-down 0.3s ease-out;
+ box-shadow: var(--wplace-shadow-notification);
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Alert styling moved to theme files */
+
+/* ========================= */
+/* Modal overlay helpers */
+/* ========================= */
+.wplace-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100002;
+ display: none;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-overlay.show {
+ display: block;
+}
+
+/* Overlay visibility helpers */
+.wplace-overlay-hidden {
+ display: none !important;
+}
+
+.wplace-overlay-visible {
+ display: block !important;
+}
+
+/* ========================= */
+/* Responsive tweaks */
+/* ========================= */
+@media (width <= 768px) {
+ #wplace-image-bot-container {
+ left: 10px;
+ width: calc(100vw - 20px);
+ max-height: calc(100vh - 20px);
+ }
+
+ #wplace-stats-container {
+ display: none !important; /* hide secondary panel on small screens */
+ }
+
+ .wplace-alert-base {
+ max-width: 90vw;
+ margin: 0 5vw;
+ }
+
+ .wplace-turnstile-overlay {
+ bottom: 10px !important;
+ right: 10px !important;
+ left: 10px !important;
+ min-width: auto !important;
+ }
+}
+
+/* Auto light/dark support moved to theme files */
+
+/* ===================================== */
+/* Settings container variants (optional) */
+/* ===================================== */
+.wplace-settings-container-base {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 100001;
+ display: none;
+ min-width: 400px;
+ max-width: 500px;
+ max-height: 100vh;
+ overflow-y: auto;
+ animation: slide-in 0.3s ease-out;
+ border: 1px solid var(--wplace-border-ghost);
+ padding: 0;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-settings-container-base.show {
+ display: block;
+}
+
+/* ===================================== */
+/* Utility styles */
+/* ===================================== */
+.wplace-paint-effect {
+ animation: pulse 0.5s ease-out;
+}
+
+.wplace-settings-error {
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-stats-container.hidden {
+ display: none;
+}
+
+/* Theme-specific effects are now in separate theme files */
+
+/* ===================================== */
+/* Settings Dialog Styles */
+/* ===================================== */
+
+/* Settings content container */
+.wplace-settings-content {
+ padding: 25px 25px 0;
+ max-height: 67vh;
+ overflow-y: auto;
+}
+
+/* Settings section containers */
+.wplace-settings-section {
+ margin-bottom: 25px;
+}
+
+/* Section labels */
+.wplace-settings-section-label {
+ margin-bottom: 12px;
+ color: var(--wplace-text);
+ font-weight: 500;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+/* Icon colors */
+.wplace-icon-key {
+ font-size: 16px;
+}
+
+.wplace-icon-robot {
+ font-size: 16px;
+}
+
+.wplace-icon-speed {
+ font-size: 16px;
+}
+
+.wplace-icon-bell {
+ font-size: 16px;
+}
+
+.wplace-icon-palette {
+ font-size: 16px;
+}
+
+.wplace-icon-globe {
+ font-size: 16px;
+}
+
+.wplace-icon-paint {
+ font-size: 16px;
+}
+
+/* Section wrapper styling */
+.wplace-settings-section-wrapper {
+ padding: 18px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Select dropdowns */
+.wplace-settings-select {
+ width: 100%;
+ padding: 12px 16px;
+ font-size: 14px;
+ outline: none;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-family: inherit;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-settings-option {
+ padding: 10px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Settings description text */
+.wplace-settings-description {
+ font-size: 12px;
+ color: var(--wplace-text-muted);
+ margin: 8px 0 0;
+}
+
+/* Batch Controls */
+.wplace-batch-controls {
+ padding: 18px;
+ margin-bottom: 15px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-random-batch-controls {
+ display: none;
+}
+
+/* Speed Slider Container */
+.wplace-speed-slider-container {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ margin-bottom: 10px;
+}
+
+
+.wplace-speed-value {
+ min-width: 100px;
+ text-align: center;
+ padding: 8px 12px;
+ font-weight: bold;
+ font-size: 13px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-speed-labels {
+ display: flex;
+ justify-content: space-between;
+ color: var(--wplace-text-muted);
+ font-size: 11px;
+ margin-top: 8px;
+}
+
+/* Random Batch Controls */
+.wplace-random-batch-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 15px;
+}
+
+.wplace-random-batch-label {
+ display: block;
+ color: var(--wplace-text-faded);
+ font-size: 12px;
+ margin-bottom: 8px;
+}
+
+.wplace-icon-min {
+ color: var(--wplace-icon-primary);
+ margin-right: 4px;
+}
+
+.wplace-icon-max {
+ color: var(--wplace-icon-secondary);
+ margin-right: 4px;
+}
+
+.wplace-settings-number-input {
+ width: 100%;
+ padding: 10px 12px;
+ background: var(--wplace-bg-input);
+ color: var(--wplace-text);
+ border: 1px solid var(--wplace-border-subtle);
+ border-radius: 8px;
+ font-size: 13px;
+ outline: none;
+}
+
+.wplace-random-batch-description {
+ font-size: 11px;
+ color: var(--wplace-text-dim);
+ margin: 8px 0 0;
+ text-align: center;
+}
+
+/* Speed Control Toggle */
+.wplace-speed-control-toggle {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--wplace-text);
+}
+
+.wplace-speed-checkbox {
+ cursor: pointer;
+}
+
+/* Overlay Settings */
+.wplace-overlay-opacity-control {
+ margin-bottom: 15px;
+}
+
+.wplace-overlay-opacity-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.wplace-overlay-opacity-label {
+ font-weight: 500;
+ font-size: 13px;
+}
+
+.wplace-overlay-opacity-value {
+ min-width: 40px;
+ text-align: center;
+ background: var(--wplace-accent);
+ color: var(--wplace-text);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 12px;
+ border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
+}
+
+.wplace-overlay-opacity-slider {
+ width: 100%;
+ -webkit-appearance: none;
+ height: 8px;
+ outline: none;
+ cursor: pointer;
+}
+
+.wplace-settings-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+}
+
+.wplace-settings-toggle-title {
+ font-weight: 500;
+}
+
+.wplace-settings-toggle-description {
+ font-size: 12px;
+ margin: 4px 0 0;
+ color: var(--wplace-muted-text);
+}
+
+.wplace-settings-checkbox {
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ flex-shrink: 0;
+ accent-color: var(--wplace-highlight);
+}
+
+/* Notifications */
+.wplace-notifications-wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.wplace-notification-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.wplace-notification-checkbox {
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+}
+
+.wplace-notification-interval {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.wplace-notification-interval-input {
+ width: 70px;
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid var(--wplace-border-subtle);
+ background: var(--wplace-bg-faint);
+ color: var(--wplace-text);
+}
+
+.wplace-notification-buttons {
+ display: flex;
+ gap: 10px;
+}
+
+.wplace-notification-perm-btn,
+.wplace-notification-test-btn {
+ flex: 1;
+}
+
+/* Settings Footer */
+.wplace-settings-footer {
+ border-top: 1px solid var(--wplace-border-ghost);
+ padding: 20px;
+ position: sticky;
+ bottom: 0;
+ background: var(--wplace-secondary);
+}
+
+/* Settings select option styling moved to theme files */
+/* Settings description styling moved to theme files */
+
+.wplace-settings-apply-btn {
+ width: 100%;
+ border: none;
+ padding: 10px 16px;
+ cursor: pointer;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ justify-content: center;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* ===================================== */
+/* Resize Dialog Styles */
+/* ===================================== */
+
+/* Resize dialog title */
+.resize-dialog-title {
+ margin-top: 0;
+ color: var(--wplace-text);
+}
+
+/* Resize control labels */
+.resize-control-label {
+ display: block;
+ margin-bottom: 8px;
+ font-size: 14px;
+}
+
+.resize-checkbox-label {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ font-size: 14px;
+ gap: 8px;
+}
+
+/* Zoom controls */
+
+.resize-zoom-btn {
+ padding: 4px 8px;
+}
+
+.resize-zoom-slider {
+ max-width: 220px;
+}
+
+.resize-zoom-value {
+ margin-left: 6px;
+ min-width: 48px;
+ text-align: right;
+ opacity: 0.85;
+ font-size: 12px;
+}
+
+.resize-camera-help {
+ font-size: 11px;
+ opacity: 0.75;
+ margin-left: auto;
+}
+
+/* Canvas positioning */
+.resize-pan-stage {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+.resize-canvas-positioned {
+ position: absolute;
+ left: 0;
+ top: 0;
+ transform-origin: top left;
+}
+
+/* Resize tools */
+.resize-tools-container {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+ align-items: center;
+}
+
+.resize-brush-controls {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.resize-brush-control {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ justify-content: space-between;
+}
+
+.resize-tool-label {
+ font-size: 12px;
+ opacity: 0.85;
+}
+
+.resize-tool-input-group {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.resize-tool-slider {
+ width: 120px;
+}
+
+.resize-tool-value {
+ font-size: 12px;
+ opacity: 0.85;
+ min-width: 18px;
+ text-align: center;
+}
+
+.resize-mode-controls {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.resize-mode-group {
+ display: flex;
+ gap: 6px;
+}
+
+.resize-mode-btn,
+.resize-clear-btn,
+.resize-invert-btn {
+ padding: 4px 8px;
+ font-size: 12px;
+}
+
+.resize-shortcut-help {
+ opacity: 0.8;
+ font-size: 12px;
+}
+
+/* Color palette section */
+.resize-color-palette-section {
+ margin-top: 15px;
+}
+
+.resize-color-toggle-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+}
+
+.resize-color-checkbox {
+ cursor: pointer;
+}
+
+/* Advanced color section */
+.resize-advanced-color-section {
+ margin-top: 15px;
+}
+
+.resize-advanced-controls {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.resize-advanced-label {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ font-size: 12px;
+}
+
+.resize-advanced-label-text {
+ font-weight: 600;
+}
+
+.resize-advanced-select {
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid var(--wplace-border-faint);
+ background: var(--wplace-bg-whisper);
+ color: var(--wplace-text);
+}
+
+.resize-advanced-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 12px;
+}
+
+.resize-advanced-toggle-content {
+ flex: 1;
+}
+
+.resize-advanced-description {
+ margin-top: 2px;
+ opacity: 0.65;
+}
+
+.resize-advanced-checkbox {
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+}
+
+.resize-chroma-weight-control {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.resize-chroma-weight-header {
+ display: flex;
+ justify-content: space-between;
+ font-size: 11px;
+ margin-bottom: 4px;
+}
+
+.resize-chroma-weight-value {
+ background: var(--wplace-bg-faint);
+ padding: 2px 6px;
+ border-radius: 4px;
+}
+
+.resize-chroma-weight-slider {
+ width: 100%;
+}
+
+.resize-threshold-controls {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+}
+
+.resize-threshold-label {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ font-size: 12px;
+}
+
+.resize-threshold-input {
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid var(--wplace-border-faint);
+ background: var(--wplace-bg-whisper);
+ color: var(--wplace-text);
+}
+
+.resize-reset-advanced-btn {
+ background: linear-gradient(135deg, var(--wplace-danger), var(--wplace-danger-dark));
+ font-size: 11px;
+}
+
+/* ===================================== */
+/* Additional styles for exact upstream match */
+/* ===================================== */
+
+/* Overlay styles exact match */
+.resize-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 9999;
+ display: none;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Settings animations - removed duplicate, handled above */
+
+/* Settings Header Styling */
+.wplace-settings-header {
+ background: var(--wplace-accent);
+ padding: 20px;
+ border-bottom: 1px solid var(--wplace-border-color);
+ cursor: move;
+}
+
+.wplace-settings-title-wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.wplace-settings-title {
+ margin: 0;
+ color: var(--wplace-text);
+ font-size: 20px;
+ font-weight: 300;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.wplace-settings-icon {
+ font-size: 18px;
+ color: var(--wplace-highlight);
+ animation: spin 2s linear infinite;
+}
+
+.wplace-settings-close-btn {
+ background: rgb(34 34 34 / 40%);
+ color: var(--wplace-text);
+ border: 1px solid var(--wplace-border-color);
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s ease;
+ font-size: 14px;
+ font-weight: 300;
+}
+
+.wplace-settings-close-btn:hover {
+ background: rgb(255 0 0 / 40%);
+ transform: scale(1.1);
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* Font styling corrections */
+.resize-controls label {
+ font-size: 12px;
+ color: var(--wplace-text);
+}
+
+
+/* Additional precision fixes for exact upstream main matching */
+/* Status default styling moved to theme files */
+
+/* Duplicate settings section removed - all settings styling moved to theme files */
+
+/* Settings header styling moved to theme files */
+/* All remaining settings styling moved to theme files */
+
+
+/* Icon styling moved to theme files */
+.wplace-icon-eye {
+ font-size: 16px;
+}
+
+/* Overlay Settings Controls */
+.wplace-overlay-opacity-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: var(--wplace-slider-thumb-bg);
+ box-shadow: var(--wplace-shadow-slider-thumb);
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.wplace-overlay-opacity-slider::-webkit-slider-thumb:hover {
+ transform: scale(1.2);
+ box-shadow: var(--wplace-shadow-slider-hover);
+}
+
+/* Coordinate Generation Controls */
+.wplace-icon-route {
+ color: var(--wplace-icon-primary);
+ font-size: 16px;
+}
+
+.wplace-icon-table {
+ color: var(--wplace-icon-palette);
+ margin-right: 6px;
+}
+
+.wplace-icon-compass {
+ color: var(--wplace-icon-secondary);
+ margin-right: 6px;
+}
+
+/* Pixel Filter Controls */
+.wplace-pixel-filter-controls {
+ padding: 18px;
+ margin-bottom: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Snake Pattern Controls */
+.wplace-snake-pattern-controls {
+ padding: 18px;
+ margin-bottom: 15px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+/* Block Size Controls */
+.wplace-block-size-controls {
+ padding: 18px;
+ margin-bottom: 15px;
+
+ /* Theme-specific styling applied via theme files */
+}
+
+.wplace-block-size-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 15px;
+}
+
+.wplace-block-size-label {
+ display: block;
+ color: var(--wplace-text-faded);
+ font-size: 12px;
+ margin-bottom: 8px;
+}
+
+.wplace-icon-width {
+ color: var(--wplace-icon-primary);
+ margin-right: 4px;
+}
+
+.wplace-icon-height {
+ color: var(--wplace-icon-secondary);
+ margin-right: 4px;
+}
+
+.wplace-block-size-description {
+ font-size: 11px;
+ color: var(--wplace-text-dim);
+ margin: 8px 0 0;
+ text-align: center;
+}
+
+/* Random Block Controls */
+.wplace-shuffle-block-size-controls {
+ display: none;
+}
+
+/* Batch Mode Controls */
+.wplace-mode-selection {
+ margin-bottom: 15px;
+}
+
+.wplace-mode-label {
+ display: block;
+ margin-bottom: 8px;
+ color: var(--wplace-text-secondary);
+ font-weight: 500;
+ font-size: 14px;
+}
+
+.wplace-icon-dice {
+ color: var(--wplace-icon-palette);
+ margin-right: 6px;
+}
diff --git a/lang/en.json b/lang/en.json
new file mode 100644
index 00000000..d2c493d2
--- /dev/null
+++ b/lang/en.json
@@ -0,0 +1,154 @@
+{
+ "title": "Auto-Image",
+ "toggleOverlay": "Toggle Overlay",
+ "scanColors": "Scan Colors",
+ "uploadImage": "Upload",
+ "resizeImage": "Resize",
+ "selectPosition": "Select Position",
+ "startPainting": "Start",
+ "stopPainting": "Stop",
+ "checkingColors": "🔍 Checking available colors...",
+ "noColorsFound": "❌ To update the color swatch, open the color palette on the site and try again!",
+ "colorsUpdated": "✅ Available colors increased {oldCount} -> {newCount}, {diffCount} new colors found",
+ "colorsFound": "✅ {count} available colors found. Ready to upload.",
+ "loadingImage": "🖼️ Loading image...",
+ "imageLoaded": "✅ Image loaded with {count} valid pixels",
+ "imageError": "❌ Error loading image",
+ "selectPositionAlert": "Paint the first pixel at the location where you want the art to start!",
+ "waitingPosition": "👆 Waiting for you to paint the reference pixel...",
+ "positionSet": "✅ Position set successfully!",
+ "positionTimeout": "❌ Timeout for position selection",
+ "startPaintingMsg": "🎨 Starting painting...",
+ "paintingProgress": "🧱 Progress: {painted}/{total} pixels...",
+ "noCharges": "⌛ No charges. Waiting {time}...",
+ "overlayTilesNotLoaded": "❌ Required map tiles not loaded. Check connection or retry.",
+ "paintingStoppedByUser": "⏹️ Painting stopped by user",
+ "paintingBatchFailed": "❌ Failed to send pixel batch after retries. Painting stopped.",
+ "paintingPixelCheckFailed": "❌ Failed to read pixel at ({x}, {y}). Painting stopped.",
+ "paintingFinalBatchFailed": "⚠️ Final batch of {count} pixels failed after retries.",
+ "paintingComplete": "✅ Painting complete! {count} pixels painted.",
+ "paintingError": "❌ Unexpected error during painting",
+ "missingRequirements": "❌ Load an image and select a position first",
+ "progress": "Progress",
+ "pixels": "Pixels",
+ "charges": "Charges",
+ "fullChargeIn": "Full Charge In",
+ "estimatedTime": "Estimated time",
+ "initMessage": "Click 'Upload Image' to begin",
+ "waitingInit": "Waiting for initialization...",
+ "initializingToken": "🔧 Initializing Turnstile token generator...",
+ "tokenReady": "✅ Token generator ready - you can now start painting!",
+ "tokenRetryLater": "⚠️ Token generator will retry when needed",
+ "resizeSuccess": "✅ Image resized to {width}x{height}",
+ "paintingPaused": "⏸️ Painting paused at position X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Token generation failed. Please try again in a moment.",
+ "saveData": "Save Progress",
+ "loadData": "Load Progress",
+ "saveToFile": "Save to File",
+ "loadFromFile": "Load from File",
+ "dataManager": "Data Manager",
+ "autoSaved": "✅ Progress saved automatically",
+ "dataLoaded": "✅ Progress loaded successfully",
+ "fileSaved": "✅ Progress saved to file successfully",
+ "fileLoaded": "✅ Progress loaded from file successfully",
+ "noSavedData": "❌ No saved progress found",
+ "savedDataFound": "✅ Saved progress found! Load to continue?",
+ "savedDate": "Saved on: {date}",
+ "clickLoadToContinue": "Click 'Load Progress' to continue.",
+ "fileError": "❌ Error processing file",
+ "invalidFileFormat": "❌ Invalid file format",
+ "paintingSpeed": "Painting Speed",
+ "pixelsPerSecond": "pixels/second",
+ "speedSetting": "Speed: {speed} pixels/sec",
+ "settings": "Settings",
+ "botSettings": "Bot Settings",
+ "close": "Close",
+ "language": "Language",
+ "themeSettings": "Theme Settings",
+ "themeSettingsDesc": "Choose your preferred color theme for the interface.",
+ "languageSelectDesc": "Select your preferred language. Changes will take effect immediately.",
+ "autoCaptcha": "Auto-CAPTCHA Solver (Turnstile)",
+ "autoCaptchaDesc": "Automatically generates Turnstile tokens using integrated generator. Falls back to browser automation if needed.",
+ "applySettings": "Apply Settings",
+ "settingsSaved": "✅ Settings saved successfully!",
+ "speedOn": "On",
+ "speedOff": "Off",
+ "cooldownSettings": "Cooldown Settings",
+ "waitCharges": "Wait until charges reach",
+ "captchaSolving": "🔑 Generating Turnstile token...",
+ "captchaFailed": "❌ Turnstile token generation failed. Trying fallback method...",
+ "automation": "Automation",
+ "noChargesThreshold": "⌛ Waiting to reach {threshold} charges. Currently {current}. Estimated time: {time}.",
+ "tokenCapturedSuccess": "Token captured successfully! You can start the bot now.",
+ "notificationsNotSupported": "Notifications are not supported in this browser.",
+ "chargesReadyNotification": "WPlace — Charges Ready",
+ "chargesReadyMessage": "Charges ready: {current} / {max}. Threshold: {threshold}.",
+ "testNotificationTitle": "WPlace — Test",
+ "testNotificationMessage": "This is a test notification.",
+ "showStats": "Show Stats",
+ "compactMode": "Compact Mode",
+ "refreshCharges": "Refresh Charges",
+ "closeStats": "Close Stats",
+ "zoomOut": "Zoom Out",
+ "zoomIn": "Zoom In",
+ "fitToView": "Fit to view",
+ "actualSize": "Actual size (100%)",
+ "panMode": "Pan (drag to move view)",
+ "clearIgnoredPixels": "Clear all ignored pixels",
+ "invertMask": "Invert mask",
+ "waitingSetupComplete": "🔄 Waiting for initial setup to complete...",
+ "waitingTokenGenerator": "🔄 Waiting for token generator to initialize...",
+ "uploadImageFirst": "Upload an image first to capture available colors",
+ "pleaseWaitInitialSetup": "🔄 Please wait for the initial setup to complete before loading progress.",
+ "pleaseWaitFileSetup": "🔄 Please wait for the initial setup to complete before loading from file.",
+ "errorSavingProgress": "❌ Error saving progress",
+ "errorLoadingProgress": "❌ Error loading progress",
+ "fileOperationsAvailable": "📂 File operations (Load/Upload) are now available!",
+ "tokenGeneratorReady": "🔑 Token generator ready!",
+ "paintingStats": "Painting Stats",
+ "enablePaintingSpeedLimit": "Enable painting speed limit (batch size control)",
+ "enableNotifications": "Enable notifications",
+ "notifyOnChargesThreshold": "Notify when charges reach threshold",
+ "onlyWhenNotFocused": "Only when tab is not focused",
+ "repeatEvery": "Repeat every",
+ "minutesPl": "minute(s)",
+ "grantPermission": "Grant Permission",
+ "test": "Test",
+ "showAllColorsIncluding": "Show All Colors (including unavailable)",
+ "chromaWeight": "Chroma Weight",
+ "downloadPreview": "Download Preview",
+ "apply": "Apply",
+ "cancel": "Cancel",
+ "fit": "Fit",
+ "hundred": "100%",
+ "clear": "Clear",
+ "invert": "Invert",
+ "reprocessingOverlay": "Re-processing overlay...",
+ "overlayUpdated": "Overlay updated!",
+ "notificationsEnabled": "Notifications enabled.",
+ "notificationsPermissionDenied": "Notifications permission denied.",
+ "overlayEnabled": "Overlay enabled.",
+ "overlayDisabled": "Overlay disabled.",
+ "tokenSourceSet": "Token source set to: {source}",
+ "batchModeSet": "Batch mode set to: {mode}",
+ "randomRange": "Random Range",
+ "normalFixedSize": "Normal Fixed Size",
+ "advancedColorSettingsReset": "Advanced color settings reset.",
+ "shiftRowAltColumn": "Shift = Row • Alt = Column",
+ "hideTurnstileBtn": "Hide",
+ "turnstileInstructions": "Cloudflare Turnstile — please complete the check if shown",
+ "uploadImageFirstColors": "Please upload an image first to capture available colors",
+ "availableColors": "Available Colors ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Expand Mode",
+ "minimize": "Minimize",
+ "restore": "Restore",
+ "hideStats": "Hide Stats",
+ "paintOptions": "Paint Options",
+ "paintWhitePixels": "Paint White",
+ "paintWhitePixelsDescription": "If enabled, template white pixels will be painted.",
+ "paintTransparentPixels": "Paint Transparent",
+ "paintTransparentPixelsDescription": "If enabled, template transparent pixels will be painted",
+ "paintUnavailablePixels": "Paint Unavailable",
+ "paintUnavailablePixelsDescription": "If enabled, template colors that are unavailable will be painted using the closest available color"
+}
\ No newline at end of file
diff --git a/lang/fr.json b/lang/fr.json
new file mode 100644
index 00000000..8d0d2f52
--- /dev/null
+++ b/lang/fr.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Auto-Image",
+ "toggleOverlay": "Basculer l'overlay",
+ "scanColors": "Scanner les couleurs",
+ "uploadImage": "Télécharger l'image",
+ "resizeImage": "Redimensionner l'image",
+ "selectPosition": "Sélectionner la position",
+ "startPainting": "Commencer à peindre",
+ "stopPainting": "Arrêter de peindre",
+ "checkingColors": "🔍 Vérification des couleurs disponibles...",
+ "noColorsFound": "❌ Ouvrez la palette de couleurs sur le site et réessayez!",
+ "colorsFound": "✅ {count} couleurs trouvées. Prêt à télécharger.",
+ "loadingImage": "🖼️ Chargement de l'image...",
+ "imageLoaded": "✅ Image chargée avec {count} pixels valides",
+ "imageError": "❌ Erreur lors du chargement de l'image",
+ "selectPositionAlert": "Peignez le premier pixel à l'endroit où vous voulez que l'art commence!",
+ "waitingPosition": "👆 En attente que vous peigniez le pixel de référence...",
+ "positionSet": "✅ Position définie avec succès!",
+ "positionTimeout": "❌ Délai d'attente pour la sélection de position",
+ "startPaintingMsg": "🎨 Début de la peinture...",
+ "paintingProgress": "🧱 Progrès: {painted}/{total} pixels...",
+ "noCharges": "⌛ Aucune charge. En attente {time}...",
+ "paintingStopped": "⏹️ Peinture arrêtée par l'utilisateur",
+ "paintingComplete": "✅ Peinture terminée! {count} pixels peints.",
+ "paintingError": "❌ Erreur pendant la peinture",
+ "missingRequirements": "❌ Veuillez charger une image et sélectionner une position d'abord",
+ "progress": "Progrès",
+ "pixels": "Pixels",
+ "charges": "Charges",
+ "estimatedTime": "Temps estimé",
+ "initMessage": "Cliquez sur 'Télécharger l'image' pour commencer",
+ "waitingInit": "En attente d'initialisation...",
+ "initializingToken": "🔧 Initialisation du générateur de tokens Turnstile...",
+ "tokenReady": "✅ Générateur de tokens prêt - vous pouvez commencer à peindre!",
+ "tokenRetryLater": "⚠️ Le générateur de tokens réessaiera si nécessaire",
+ "resizeSuccess": "✅ Image redimensionnée en {width}x{height}",
+ "paintingPaused": "⏸️ Peinture en pause à la position X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Échec de la génération de token. Veuillez réessayer dans un moment.",
+ "saveData": "Sauvegarder le progrès",
+ "loadData": "Charger le progrès",
+ "saveToFile": "Sauvegarder dans un fichier",
+ "loadFromFile": "Charger depuis un fichier",
+ "dataManager": "Données",
+ "autoSaved": "✅ Progrès sauvegardé automatiquement",
+ "dataLoaded": "✅ Progrès chargé avec succès",
+ "fileSaved": "✅ Sauvegardé dans un fichier avec succès",
+ "fileLoaded": "✅ Chargé depuis un fichier avec succès",
+ "noSavedData": "❌ Aucun progrès sauvegardé trouvé",
+ "savedDataFound": "✅ Progrès sauvegardé trouvé! Charger pour continuer?",
+ "savedDate": "Sauvegardé le: {date}",
+ "clickLoadToContinue": "Cliquez sur 'Charger le progrès' pour continuer.",
+ "fileError": "❌ Erreur lors du traitement du fichier",
+ "invalidFileFormat": "❌ Format de fichier invalide",
+ "paintingSpeed": "Vitesse de peinture",
+ "pixelsPerSecond": "pixels/seconde",
+ "speedSetting": "Vitesse: {speed} pixels/sec",
+ "settings": "Paramètres",
+ "botSettings": "Paramètres du Bot",
+ "close": "Fermer",
+ "language": "Langue",
+ "themeSettings": "Paramètres de Thème",
+ "themeSettingsDesc": "Choisissez votre thème de couleurs préféré pour l'interface.",
+ "languageSelectDesc": "Sélectionnez votre langue préférée. Les changements prendront effet immédiatement.",
+ "autoCaptcha": "Résolveur de CAPTCHA automatique (Turnstile)",
+ "autoCaptchaDesc": "Génère automatiquement des jetons Turnstile en utilisant le générateur intégré. Se replie sur l'automatisation du navigateur si nécessaire.",
+ "applySettings": "Appliquer les paramètres",
+ "settingsSaved": "✅ Paramètres enregistrés avec succès !",
+ "speedOn": "Activé",
+ "speedOff": "Désactivé",
+ "cooldownSettings": "Paramètres de recharge",
+ "waitCharges": "Attendre que les charges atteignent",
+ "captchaSolving": "🔑 Génération du jeton Turnstile...",
+ "captchaFailed": "❌ Échec de génération du jeton Turnstile. Tentative de méthode alternative...",
+ "automation": "Automatisation",
+ "noChargesThreshold": "⌛ En attente que les charges atteignent {threshold}. Actuel: {current}. Prochaine dans {time}...",
+ "tokenCapturedSuccess": "Jeton capturé avec succès ! Vous pouvez démarrer le bot maintenant.",
+ "notificationsNotSupported": "Les notifications ne sont pas supportées dans ce navigateur.",
+ "chargesReadyNotification": "WPlace — Charges Prêtes",
+ "chargesReadyMessage": "Charges prêtes : {current} / {max}. Seuil : {threshold}.",
+ "testNotificationTitle": "WPlace — Test",
+ "testNotificationMessage": "Ceci est une notification de test.",
+ "showStats": "Afficher les Stats",
+ "compactMode": "Mode Compact",
+ "refreshCharges": "Actualiser les Charges",
+ "closeStats": "Fermer les Stats",
+ "zoomOut": "Dézoomer",
+ "zoomIn": "Zoomer",
+ "fitToView": "Ajuster à la vue",
+ "actualSize": "Taille réelle (100%)",
+ "panMode": "Panoramique (glisser pour déplacer la vue)",
+ "clearIgnoredPixels": "Effacer tous les pixels ignorés",
+ "invertMask": "Inverser le masque",
+ "waitingSetupComplete": "🔄 En attente de la fin de l'installation initiale...",
+ "waitingTokenGenerator": "🔄 En attente de l'initialisation du générateur de jetons...",
+ "uploadImageFirst": "Téléchargez d'abord une image pour capturer les couleurs disponibles",
+ "pleaseWaitInitialSetup": "🔄 Veuillez attendre la fin de l'installation initiale avant de charger les progrès.",
+ "pleaseWaitFileSetup": "🔄 Veuillez attendre la fin de l'installation initiale avant de charger depuis un fichier.",
+ "errorSavingProgress": "❌ Erreur lors de la sauvegarde des progrès",
+ "errorLoadingProgress": "❌ Erreur lors du chargement des progrès",
+ "fileOperationsAvailable": "📂 Les opérations sur fichiers (Charger/Télécharger) sont maintenant disponibles !",
+ "tokenGeneratorReady": "🔑 Générateur de jetons prêt !",
+ "paintingStats": "Statistiques de Peinture",
+ "enablePaintingSpeedLimit": "Activer la limite de vitesse de peinture (contrôle de la taille de lot)",
+ "enableNotifications": "Activer les notifications",
+ "notifyOnChargesThreshold": "Notifier quand les charges atteignent le seuil",
+ "onlyWhenNotFocused": "Seulement quand l'onglet n'est pas au premier plan",
+ "repeatEvery": "Répéter toutes les",
+ "minutesPl": "minute(s)",
+ "grantPermission": "Accorder la Permission",
+ "test": "Test",
+ "showAllColorsIncluding": "Afficher toutes les couleurs (y compris indisponibles)",
+ "chromaWeight": "Poids de Chrominance",
+ "downloadPreview": "Télécharger l'Aperçu",
+ "apply": "Appliquer",
+ "cancel": "Annuler",
+ "fit": "Ajuster",
+ "hundred": "100%",
+ "clear": "Effacer",
+ "invert": "Inverser",
+ "reprocessingOverlay": "Retraitement de l'overlay...",
+ "overlayUpdated": "Overlay mis à jour !",
+ "notificationsEnabled": "Notifications activées.",
+ "notificationsPermissionDenied": "Permission de notifications refusée.",
+ "overlayEnabled": "Overlay activé.",
+ "overlayDisabled": "Overlay désactivé.",
+ "tokenSourceSet": "Source de jeton définie à : {source}",
+ "batchModeSet": "Mode lot défini à : {mode}",
+ "randomRange": "Plage Aléatoire",
+ "normalFixedSize": "Taille Fixe Normale",
+ "advancedColorSettingsReset": "Paramètres de couleur avancés réinitialisés.",
+ "shiftRowAltColumn": "Shift = Ligne • Alt = Colonne",
+ "hideTurnstileBtn": "Masquer",
+ "turnstileInstructions": "Cloudflare Turnstile — veuillez compléter la vérification si affichée",
+ "uploadImageFirstColors": "Veuillez d'abord télécharger une image pour capturer les couleurs disponibles",
+ "availableColors": "Couleurs Disponibles ({count})",
+ "colorTooltip": "ID : {id}\nRVB : {rgb}",
+ "expandMode": "Mode Étendu",
+ "minimize": "Réduire",
+ "restore": "Restaurer",
+ "hideStats": "Masquer les Stats",
+ "paintOptions": "Options de peinture",
+ "paintWhitePixels": "Peindre les pixels blancs",
+ "paintTransparentPixels": "Peindre les pixels transparents"
+}
diff --git a/lang/id.json b/lang/id.json
new file mode 100644
index 00000000..9cad6e2b
--- /dev/null
+++ b/lang/id.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Auto-Image",
+ "toggleOverlay": "Toggle Overlay",
+ "scanColors": "Pindai Warna",
+ "uploadImage": "Unggah Gambar",
+ "resizeImage": "Ubah Ukuran Gambar",
+ "selectPosition": "Pilih Posisi",
+ "startPainting": "Mulai Melukis",
+ "stopPainting": "Berhenti Melukis",
+ "checkingColors": "🔍 Memeriksa warna yang tersedia...",
+ "noColorsFound": "❌ Buka palet warna di situs dan coba lagi!",
+ "colorsFound": "✅ {count} warna ditemukan. Siap untuk diunggah.",
+ "loadingImage": "🖼️ Memuat gambar...",
+ "imageLoaded": "✅ Gambar dimuat dengan {count} piksel valid",
+ "imageError": "❌ Kesalahan saat memuat gambar",
+ "selectPositionAlert": "Lukis piksel pertama di lokasi tempat karya seni akan dimulai!",
+ "waitingPosition": "👆 Menunggu Anda melukis piksel referensi...",
+ "positionSet": "✅ Posisi berhasil diatur!",
+ "positionTimeout": "❌ Waktu habis untuk memilih posisi",
+ "startPaintingMsg": "🎨 Mulai melukis...",
+ "paintingProgress": "🧱 Progres: {painted}/{total} piksel...",
+ "noCharges": "⌛ Tidak ada muatan. Menunggu {time}...",
+ "paintingStopped": "⏹️ Melukis dihentikan oleh pengguna",
+ "paintingComplete": "✅ Melukis selesai! {count} piksel telah dilukis.",
+ "paintingError": "❌ Kesalahan selama melukis",
+ "missingRequirements": "❌ Unggah gambar dan pilih posisi terlebih dahulu",
+ "progress": "Progres",
+ "pixels": "Piksel",
+ "charges": "Muatan",
+ "estimatedTime": "Perkiraan waktu",
+ "initMessage": "Klik 'Unggah Gambar' untuk memulai",
+ "waitingInit": "Menunggu inisialisasi...",
+ "initializingToken": "🔧 Menginisialisasi generator token Turnstile...",
+ "tokenReady": "✅ Generator token siap - Anda bisa mulai melukis!",
+ "tokenRetryLater": "⚠️ Generator token akan mencoba lagi saat diperlukan",
+ "resizeSuccess": "✅ Gambar berhasil diubah ukurannya menjadi {width}x{height}",
+ "paintingPaused": "⏸️ Melukis dijeda di posisi X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Pembuatan token gagal. Silakan coba lagi sebentar lagi.",
+ "saveData": "Simpan Progres",
+ "loadData": "Muat Progres",
+ "saveToFile": "Simpan ke File",
+ "loadFromFile": "Muat dari File",
+ "dataManager": "Data",
+ "autoSaved": "✅ Progres disimpan secara otomatis",
+ "dataLoaded": "✅ Progres berhasil dimuat",
+ "fileSaved": "✅ Berhasil disimpan ke file",
+ "fileLoaded": "✅ Berhasil dimuat dari file",
+ "noSavedData": "❌ Tidak ditemukan progres yang disimpan",
+ "savedDataFound": "✅ Progres yang disimpan ditemukan! Muat untuk melanjutkan?",
+ "savedDate": "Disimpan pada: {date}",
+ "clickLoadToContinue": "Klik 'Muat Progres' untuk melanjutkan.",
+ "fileError": "❌ Kesalahan saat memproses file",
+ "invalidFileFormat": "❌ Format file tidak valid",
+ "paintingSpeed": "Kecepatan Melukis",
+ "pixelsPerSecond": "piksel/detik",
+ "speedSetting": "Kecepatan: {speed} piksel/detik",
+ "settings": "Pengaturan",
+ "botSettings": "Pengaturan Bot",
+ "close": "Tutup",
+ "language": "Bahasa",
+ "themeSettings": "Pengaturan Tema",
+ "themeSettingsDesc": "Pilih tema warna favorit Anda untuk antarmuka.",
+ "languageSelectDesc": "Pilih bahasa yang Anda inginkan. Perubahan akan berlaku segera.",
+ "autoCaptcha": "Penyelesai CAPTCHA Otomatis",
+ "autoCaptchaDesc": "Mencoba menyelesaikan CAPTCHA secara otomatis dengan mensimulasikan penempatan piksel manual saat token kedaluwarsa.",
+ "applySettings": "Terapkan Pengaturan",
+ "settingsSaved": "✅ Pengaturan berhasil disimpan!",
+ "speedOn": "Nyala",
+ "speedOff": "Mati",
+ "cooldownSettings": "Pengaturan Cooldown",
+ "waitCharges": "Tunggu hingga muatan mencapai",
+ "captchaSolving": "🤖 Mencoba menyelesaikan CAPTCHA...",
+ "captchaFailed": "❌ Gagal menyelesaikan CAPTCHA. Lukis satu piksel secara manual.",
+ "automation": "Automasi",
+ "noChargesThreshold": "⌛ Menunggu muatan mencapai {threshold}. Saat ini: {current}. Berikutnya dalam {time}...",
+ "tokenCapturedSuccess": "Token berhasil ditangkap! Anda bisa memulai bot sekarang.",
+ "notificationsNotSupported": "Notifikasi tidak didukung di browser ini.",
+ "chargesReadyNotification": "WPlace — Muatan Siap",
+ "chargesReadyMessage": "Muatan siap: {current} / {max}. Batas: {threshold}.",
+ "testNotificationTitle": "WPlace — Tes",
+ "testNotificationMessage": "Ini adalah notifikasi tes.",
+ "showStats": "Tampilkan Statistik",
+ "compactMode": "Mode Kompak",
+ "refreshCharges": "Segarkan Muatan",
+ "closeStats": "Tutup Statistik",
+ "zoomOut": "Perkecil",
+ "zoomIn": "Perbesar",
+ "fitToView": "Sesuaikan tampilan",
+ "actualSize": "Ukuran sebenarnya (100%)",
+ "panMode": "Geser (seret untuk memindahkan tampilan)",
+ "clearIgnoredPixels": "Bersihkan semua piksel yang diabaikan",
+ "invertMask": "Balik masker",
+ "waitingSetupComplete": "🔄 Menunggu pengaturan awal selesai...",
+ "waitingTokenGenerator": "🔄 Menunggu generator token diinisialisasi...",
+ "uploadImageFirst": "Unggah gambar terlebih dahulu untuk menangkap warna yang tersedia",
+ "pleaseWaitInitialSetup": "🔄 Harap tunggu pengaturan awal selesai sebelum memuat progres.",
+ "pleaseWaitFileSetup": "🔄 Harap tunggu pengaturan awal selesai sebelum memuat dari file.",
+ "errorSavingProgress": "❌ Kesalahan menyimpan progres",
+ "errorLoadingProgress": "❌ Kesalahan memuat progres",
+ "fileOperationsAvailable": "📂 Operasi file (Muat/Unggah) sekarang tersedia!",
+ "tokenGeneratorReady": "🔑 Generator token siap!",
+ "paintingStats": "Statistik Melukis",
+ "enablePaintingSpeedLimit": "Aktifkan batas kecepatan melukis (kontrol ukuran batch)",
+ "enableNotifications": "Aktifkan notifikasi",
+ "notifyOnChargesThreshold": "Beri tahu saat muatan mencapai batas",
+ "onlyWhenNotFocused": "Hanya saat tab tidak difokuskan",
+ "repeatEvery": "Ulangi setiap",
+ "minutesPl": "menit",
+ "grantPermission": "Berikan Izin",
+ "test": "Tes",
+ "showAllColorsIncluding": "Tampilkan Semua Warna (termasuk yang tidak tersedia)",
+ "chromaWeight": "Bobot Kroma",
+ "downloadPreview": "Unduh Pratinjau",
+ "apply": "Terapkan",
+ "cancel": "Batal",
+ "fit": "Sesuaikan",
+ "hundred": "100%",
+ "clear": "Bersihkan",
+ "invert": "Balik",
+ "reprocessingOverlay": "Memproses ulang overlay...",
+ "overlayUpdated": "Overlay diperbarui!",
+ "notificationsEnabled": "Notifikasi diaktifkan.",
+ "notificationsPermissionDenied": "Izin notifikasi ditolak.",
+ "overlayEnabled": "Overlay diaktifkan.",
+ "overlayDisabled": "Overlay dinonaktifkan.",
+ "tokenSourceSet": "Sumber token diatur ke: {source}",
+ "batchModeSet": "Mode batch diatur ke: {mode}",
+ "randomRange": "Rentang Acak",
+ "normalFixedSize": "Ukuran Tetap Normal",
+ "advancedColorSettingsReset": "Pengaturan warna lanjutan direset.",
+ "shiftRowAltColumn": "Shift = Baris • Alt = Kolom",
+ "hideTurnstileBtn": "Sembunyikan",
+ "turnstileInstructions": "Cloudflare Turnstile — harap selesaikan pemeriksaan jika ditampilkan",
+ "uploadImageFirstColors": "Harap unggah gambar terlebih dahulu untuk menangkap warna yang tersedia",
+ "availableColors": "Warna Tersedia ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Mode Perluas",
+ "minimize": "Minimalkan",
+ "restore": "Pulihkan",
+ "hideStats": "Sembunyikan Statistik",
+ "paintOptions": "Opsi Pewarnaan",
+ "paintWhitePixels": "Warnai piksel putih",
+ "paintTransparentPixels": "Warnai piksel transparan"
+}
diff --git a/lang/ja.json b/lang/ja.json
new file mode 100644
index 00000000..d2fc9735
--- /dev/null
+++ b/lang/ja.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace 自動画像",
+ "toggleOverlay": "オーバーレイ切替",
+ "scanColors": "色をスキャン",
+ "uploadImage": "画像をアップロード",
+ "resizeImage": "画像サイズ変更",
+ "selectPosition": "位置を選択",
+ "startPainting": "描画開始",
+ "stopPainting": "描画停止",
+ "checkingColors": "🔍 利用可能な色を確認中...",
+ "noColorsFound": "❌ サイトでカラーパレットを開いて再試行してください!",
+ "colorsFound": "✅ 利用可能な色 {count} 件を検出。アップロード可能。",
+ "loadingImage": "🖼️ 画像を読み込み中...",
+ "imageLoaded": "✅ 画像を読み込みました。有効なピクセル {count}",
+ "imageError": "❌ 画像の読み込みエラー",
+ "selectPositionAlert": "作品を開始したい位置に最初のピクセルを置いてください!",
+ "waitingPosition": "👆 参照ピクセルの描画を待っています...",
+ "positionSet": "✅ 位置を設定しました!",
+ "positionTimeout": "❌ 位置選択のタイムアウト",
+ "startPaintingMsg": "🎨 描画を開始...",
+ "paintingProgress": "🧱 進捗: {painted}/{total} ピクセル...",
+ "noCharges": "⌛ チャージなし。{time} 待機...",
+ "paintingStopped": "⏹️ ユーザーにより停止されました",
+ "paintingComplete": "✅ 描画完了! {count} ピクセル描画。",
+ "paintingError": "❌ 描画中にエラー",
+ "missingRequirements": "❌ 先に画像を読み込み位置を選択してください",
+ "progress": "進捗",
+ "pixels": "ピクセル",
+ "charges": "チャージ",
+ "estimatedTime": "推定時間",
+ "initMessage": "「画像をアップロード」をクリックして開始",
+ "waitingInit": "初期化待機中...",
+ "initializingToken": "🔧 Turnstile トークン生成器を初期化中...",
+ "tokenReady": "✅ トークン生成器準備完了 - 描画できます!",
+ "tokenRetryLater": "⚠️ 必要に応じて再試行します",
+ "resizeSuccess": "✅ 画像を {width}x{height} にリサイズ",
+ "paintingPaused": "⏸️ X: {x}, Y: {y} で一時停止",
+ "captchaNeeded": "❗ トークン生成に失敗。少ししてから再試行してください。",
+ "saveData": "進捗を保存",
+ "loadData": "進捗を読み込み",
+ "saveToFile": "ファイルへ保存",
+ "loadFromFile": "ファイルから読み込み",
+ "dataManager": "データ管理",
+ "autoSaved": "✅ 自動保存しました",
+ "dataLoaded": "✅ 進捗を読み込みました",
+ "fileSaved": "✅ ファイルに保存しました",
+ "fileLoaded": "✅ ファイルから読み込みました",
+ "noSavedData": "❌ 保存された進捗がありません",
+ "savedDataFound": "✅ 保存された進捗が見つかりました。続行しますか?",
+ "savedDate": "保存日時: {date}",
+ "clickLoadToContinue": "「進捗を読み込み」をクリックして続行。",
+ "fileError": "❌ ファイル処理エラー",
+ "invalidFileFormat": "❌ 無効なファイル形式",
+ "paintingSpeed": "描画速度",
+ "pixelsPerSecond": "ピクセル/秒",
+ "speedSetting": "速度: {speed} ピクセル/秒",
+ "settings": "設定",
+ "botSettings": "ボット設定",
+ "close": "閉じる",
+ "language": "言語",
+ "themeSettings": "テーマ設定",
+ "themeSettingsDesc": "インターフェースの好きなカラーテーマを選択。",
+ "languageSelectDesc": "希望言語を選択。変更は即時反映されます。",
+ "autoCaptcha": "自動 CAPTCHA ソルバー",
+ "autoCaptchaDesc": "統合ジェネレーターで Turnstile トークンを自動生成し必要に応じてブラウザ自動化にフォールバック。",
+ "applySettings": "設定を適用",
+ "settingsSaved": "✅ 設定を保存しました!",
+ "speedOn": "オン",
+ "speedOff": "オフ",
+ "cooldownSettings": "クールダウン設定",
+ "waitCharges": "チャージ数が次に達するまで待機",
+ "captchaSolving": "🔑 Turnstile トークン生成中...",
+ "captchaFailed": "❌ トークン生成失敗。フォールバックを試行...",
+ "automation": "自動化",
+ "noChargesThreshold": "⌛ チャージ {threshold} を待機中。現在 {current}。次は {time} 後...",
+ "tokenCapturedSuccess": "トークンキャプチャ成功!ボットを開始できます。",
+ "notificationsNotSupported": "このブラウザでは通知がサポートされていません。",
+ "chargesReadyNotification": "WPlace — チャージ準備完了",
+ "chargesReadyMessage": "チャージ準備完了: {current} / {max}。しきい値: {threshold}。",
+ "testNotificationTitle": "WPlace — テスト",
+ "testNotificationMessage": "これはテスト通知です。",
+ "showStats": "統計表示",
+ "compactMode": "コンパクトモード",
+ "refreshCharges": "チャージ更新",
+ "closeStats": "統計を閉じる",
+ "zoomOut": "縮小",
+ "zoomIn": "拡大",
+ "fitToView": "画面に合わせる",
+ "actualSize": "実際のサイズ (100%)",
+ "panMode": "パン(ドラッグで移動)",
+ "clearIgnoredPixels": "無視されたピクセルをすべてクリア",
+ "invertMask": "マスクを反転",
+ "waitingSetupComplete": "🔄 初期セットアップの完了を待機中...",
+ "waitingTokenGenerator": "🔄 トークンジェネレータの初期化を待機中...",
+ "uploadImageFirst": "利用可能な色を取得するために最初に画像をアップロードしてください",
+ "pleaseWaitInitialSetup": "🔄 進捗を読み込む前に初期セットアップの完了をお待ちください。",
+ "pleaseWaitFileSetup": "🔄 ファイルから読み込む前に初期セットアップの完了をお待ちください。",
+ "errorSavingProgress": "❌ 進捗の保存エラー",
+ "errorLoadingProgress": "❌ 進捗の読み込みエラー",
+ "fileOperationsAvailable": "📂 ファイル操作(読み込み/アップロード)が利用可能になりました!",
+ "tokenGeneratorReady": "🔑 トークンジェネレータ準備完了!",
+ "paintingStats": "描画統計",
+ "enablePaintingSpeedLimit": "描画速度制限を有効化(バッチサイズ制御)",
+ "enableNotifications": "通知を有効化",
+ "notifyOnChargesThreshold": "チャージがしきい値に達したら通知",
+ "onlyWhenNotFocused": "タブが非アクティブの時のみ",
+ "repeatEvery": "繰り返し間隔",
+ "minutesPl": "分",
+ "grantPermission": "許可を与える",
+ "test": "テスト",
+ "showAllColorsIncluding": "すべての色を表示(利用不可含む)",
+ "chromaWeight": "彩度重み",
+ "downloadPreview": "プレビューダウンロード",
+ "apply": "適用",
+ "cancel": "キャンセル",
+ "fit": "フィット",
+ "hundred": "100%",
+ "clear": "クリア",
+ "invert": "反転",
+ "reprocessingOverlay": "オーバーレイ再処理中...",
+ "overlayUpdated": "オーバーレイ更新完了!",
+ "notificationsEnabled": "通知が有効になりました。",
+ "notificationsPermissionDenied": "通知の許可が拒否されました。",
+ "overlayEnabled": "オーバーレイが有効になりました。",
+ "overlayDisabled": "オーバーレイが無効になりました。",
+ "tokenSourceSet": "トークンソースを設定: {source}",
+ "batchModeSet": "バッチモードを設定: {mode}",
+ "randomRange": "ランダム範囲",
+ "normalFixedSize": "通常固定サイズ",
+ "advancedColorSettingsReset": "高度な色設定をリセットしました。",
+ "shiftRowAltColumn": "Shift = 行 • Alt = 列",
+ "hideTurnstileBtn": "隠す",
+ "turnstileInstructions": "Cloudflare Turnstile — 表示された場合は確認を完了してください",
+ "uploadImageFirstColors": "利用可能な色を取得するために最初に画像をアップロードしてください",
+ "availableColors": "利用可能な色 ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "展開モード",
+ "minimize": "最小化",
+ "restore": "復元",
+ "hideStats": "統計を非表示",
+ "paintOptions": "描画オプション",
+ "paintWhitePixels": "白いピクセルを描画",
+ "paintTransparentPixels": "透明ピクセルを描画"
+}
diff --git a/lang/ko.json b/lang/ko.json
new file mode 100644
index 00000000..570183be
--- /dev/null
+++ b/lang/ko.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace 자동 이미지",
+ "toggleOverlay": "오버레이 전환",
+ "scanColors": "색상 스캔",
+ "uploadImage": "이미지 업로드",
+ "resizeImage": "크기 조정",
+ "selectPosition": "위치 선택",
+ "startPainting": "그리기 시작",
+ "stopPainting": "그리기 중지",
+ "checkingColors": "🔍 사용 가능한 색상 확인 중...",
+ "noColorsFound": "❌ 사이트에서 색상 팔레트를 연 후 다시 시도하세요!",
+ "colorsFound": "✅ 사용 가능한 색상 {count}개 발견. 업로드 준비 완료.",
+ "loadingImage": "🖼️ 이미지 불러오는 중...",
+ "imageLoaded": "✅ 이미지 로드 완료. 유효 픽셀 {count}개",
+ "imageError": "❌ 이미지 로드 오류",
+ "selectPositionAlert": "작품을 시작할 위치에 첫 픽셀을 칠하세요!",
+ "waitingPosition": "👆 기준 픽셀을 칠할 때까지 대기 중...",
+ "positionSet": "✅ 위치 설정 완료!",
+ "positionTimeout": "❌ 위치 선택 시간 초과",
+ "startPaintingMsg": "🎨 그리기 시작...",
+ "paintingProgress": "🧱 진행: {painted}/{total} 픽셀...",
+ "noCharges": "⌛ 사용 가능 횟수 없음. {time} 대기...",
+ "paintingStopped": "⏹️ 사용자에 의해 중지됨",
+ "paintingComplete": "✅ 그리기 완료! {count} 픽셀 그렸습니다.",
+ "paintingError": "❌ 그리는 중 오류",
+ "missingRequirements": "❌ 먼저 이미지를 불러오고 위치를 선택하세요",
+ "progress": "진행",
+ "pixels": "픽셀",
+ "charges": "횟수",
+ "estimatedTime": "예상 시간",
+ "initMessage": "'이미지 업로드'를 클릭하여 시작",
+ "waitingInit": "초기화 대기 중...",
+ "initializingToken": "🔧 Turnstile 토큰 생성기 초기화 중...",
+ "tokenReady": "✅ 토큰 생성 준비 완료 - 그리기를 시작할 수 있습니다!",
+ "tokenRetryLater": "⚠️ 필요 시 다시 시도합니다",
+ "resizeSuccess": "✅ 이미지가 {width}x{height} 크기로 조정됨",
+ "paintingPaused": "⏸️ 위치 X: {x}, Y: {y} 에서 일시 중지",
+ "captchaNeeded": "❗ 토큰 생성 실패. 잠시 후 다시 시도하세요.",
+ "saveData": "진행 저장",
+ "loadData": "진행 불러오기",
+ "saveToFile": "파일로 저장",
+ "loadFromFile": "파일에서 불러오기",
+ "dataManager": "데이터",
+ "autoSaved": "✅ 진행 자동 저장됨",
+ "dataLoaded": "✅ 진행 불러오기 성공",
+ "fileSaved": "✅ 파일 저장 성공",
+ "fileLoaded": "✅ 파일 불러오기 성공",
+ "noSavedData": "❌ 저장된 진행 없음",
+ "savedDataFound": "✅ 저장된 진행 발견! 계속하려면 불러오시겠습니까?",
+ "savedDate": "저장 시각: {date}",
+ "clickLoadToContinue": "'진행 불러오기'를 클릭하여 계속.",
+ "fileError": "❌ 파일 처리 오류",
+ "invalidFileFormat": "❌ 잘못된 파일 형식",
+ "paintingSpeed": "그리기 속도",
+ "pixelsPerSecond": "픽셀/초",
+ "speedSetting": "속도: {speed} 픽셀/초",
+ "settings": "설정",
+ "botSettings": "봇 설정",
+ "close": "닫기",
+ "language": "언어",
+ "themeSettings": "테마 설정",
+ "themeSettingsDesc": "인터페이스용 선호 색상 테마를 선택하세요.",
+ "languageSelectDesc": "선호 언어를 선택하세요. 변경 사항은 즉시 적용됩니다.",
+ "autoCaptcha": "자동 CAPTCHA 해결",
+ "autoCaptchaDesc": "통합 생성기를 사용해 Turnstile 토큰을 자동 생성하고 필요 시 브라우저 자동화로 폴백.",
+ "applySettings": "설정 적용",
+ "settingsSaved": "✅ 설정 저장 완료!",
+ "speedOn": "켜짐",
+ "speedOff": "꺼짐",
+ "cooldownSettings": "쿨다운 설정",
+ "waitCharges": "횟수가 다음 값에 도달할 때까지 대기",
+ "captchaSolving": "🔑 Turnstile 토큰 생성 중...",
+ "captchaFailed": "❌ 토큰 생성 실패. 폴백 시도...",
+ "automation": "자동화",
+ "noChargesThreshold": "⌛ 횟수가 {threshold} 에 도달할 때까지 대기 중. 현재 {current}. 다음 {time} 후...",
+ "tokenCapturedSuccess": "토큰 생성 성공! 이제 봇을 시작할 수 있습니다.",
+ "notificationsNotSupported": "이 브라우저는 알림을 지원하지 않습니다.",
+ "chargesReadyNotification": "WPlace — 횟수 준비 완료",
+ "chargesReadyMessage": "횟수 준비 완료: {current} / {max}. 임계값: {threshold}.",
+ "testNotificationTitle": "WPlace — 테스트",
+ "testNotificationMessage": "이것은 테스트 알림입니다.",
+ "showStats": "통계 보기",
+ "compactMode": "컴팩트 모드",
+ "refreshCharges": "횟수 새로고침",
+ "closeStats": "통계 닫기",
+ "zoomOut": "축소",
+ "zoomIn": "확대",
+ "fitToView": "화면에 맞춤",
+ "actualSize": "실제 크기 (100%)",
+ "panMode": "이동 (끌어서 보기 이동)",
+ "clearIgnoredPixels": "무시된 모든 픽셀 지우기",
+ "invertMask": "마스크 반전",
+ "waitingSetupComplete": "🔄 초기 설정 완료 대기 중...",
+ "waitingTokenGenerator": "🔄 토큰 생성기 초기화 대기 중...",
+ "uploadImageFirst": "사용 가능한 색상을 얻기 위해 먼저 이미지를 업로드하세요",
+ "pleaseWaitInitialSetup": "🔄 진행도를 로드하기 전에 초기 설정 완료를 기다리세요.",
+ "pleaseWaitFileSetup": "🔄 파일에서 로드하기 전에 초기 설정 완료를 기다리세요.",
+ "errorSavingProgress": "❌ 진행 저장 오류",
+ "errorLoadingProgress": "❌ 진행 로드 오류",
+ "fileOperationsAvailable": "📂 파일 작업(로드/업로드)이 이제 사용 가능합니다!",
+ "tokenGeneratorReady": "🔑 토큰 생성기 준비 완료!",
+ "paintingStats": "그리기 통계",
+ "enablePaintingSpeedLimit": "그리기 속도 제한 활성화 (배치 크기 제어)",
+ "enableNotifications": "알림 활성화",
+ "notifyOnChargesThreshold": "횟수가 임계값에 도달하면 알림",
+ "onlyWhenNotFocused": "탭이 포커스되지 않았을 때만",
+ "repeatEvery": "반복 간격",
+ "minutesPl": "분",
+ "grantPermission": "권한 부여",
+ "test": "테스트",
+ "showAllColorsIncluding": "모든 색상 보기 (사용 불가 포함)",
+ "chromaWeight": "색도 가중치",
+ "downloadPreview": "미리보기 다운로드",
+ "apply": "적용",
+ "cancel": "취소",
+ "fit": "맞춤",
+ "hundred": "100%",
+ "clear": "지우기",
+ "invert": "반전",
+ "reprocessingOverlay": "오버레이 재처리 중...",
+ "overlayUpdated": "오버레이 업데이트 완료!",
+ "notificationsEnabled": "알림이 활성화되었습니다.",
+ "notificationsPermissionDenied": "알림 권한이 거부되었습니다.",
+ "overlayEnabled": "오버레이가 활성화되었습니다.",
+ "overlayDisabled": "오버레이가 비활성화되었습니다.",
+ "tokenSourceSet": "토큰 소스 설정: {source}",
+ "batchModeSet": "배치 모드 설정: {mode}",
+ "randomRange": "랜덤 범위",
+ "normalFixedSize": "일반 고정 크기",
+ "advancedColorSettingsReset": "고급 색상 설정이 초기화되었습니다.",
+ "shiftRowAltColumn": "Shift = 행 • Alt = 열",
+ "hideTurnstileBtn": "숨기기",
+ "turnstileInstructions": "Cloudflare Turnstile — 표시되면 검사를 완료하세요",
+ "uploadImageFirstColors": "사용 가능한 색상을 얻기 위해 먼저 이미지를 업로드하세요",
+ "availableColors": "사용 가능한 색상 ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "확장 모드",
+ "minimize": "최소화",
+ "restore": "복원",
+ "hideStats": "통계 숨김",
+ "paintOptions": "페인트 옵션",
+ "paintWhitePixels": "흰색 픽셀 칠하기",
+ "paintTransparentPixels": "투명 픽셀 칠하기"
+}
diff --git a/lang/lang/es-MX.json b/lang/lang/es-MX.json
new file mode 100644
index 00000000..8ca3e2b1
--- /dev/null
+++ b/lang/lang/es-MX.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Auto-Imagen",
+ "toggleOverlay": "Alternar superposición",
+ "scanColors": "Escanear colores",
+ "uploadImage": "Subir imagen",
+ "resizeImage": "Cambiar tamaño de imagen",
+ "selectPosition": "Seleccionar posición",
+ "startPainting": "Iniciar pintado",
+ "stopPainting": "Detener pintado",
+ "checkingColors": "🔍 Verificando colores disponibles...",
+ "noColorsFound": "❌ ¡Abre la paleta de colores en el sitio e inténtalo de nuevo!",
+ "colorsFound": "✅ Se encontraron {count} colores disponibles. Listo para subir.",
+ "loadingImage": "🖼️ Cargando imagen...",
+ "imageLoaded": "✅ Imagen cargada con {count} píxeles válidos",
+ "imageError": "❌ Error al cargar la imagen",
+ "selectPositionAlert": "¡Pinta el primer píxel en la ubicación donde quieres que comience el arte!",
+ "waitingPosition": "👆 Esperando que pintes el píxel de referencia...",
+ "positionSet": "✅ ¡Posición establecida con éxito!",
+ "positionTimeout": "❌ Tiempo de espera para seleccionar la posición agotado",
+ "startPaintingMsg": "🎨 Iniciando pintado...",
+ "paintingProgress": "🧱 Progreso: {painted}/{total} píxeles...",
+ "noCharges": "⌛ Sin cargas. Esperando {time}...",
+ "paintingStopped": "⏹️ Pintado detenido por el usuario",
+ "paintingComplete": "✅ ¡Pintado completo! Se pintaron {count} píxeles.",
+ "paintingError": "❌ Error durante el pintado",
+ "missingRequirements": "❌ Primero carga una imagen y selecciona una posición",
+ "progress": "Progreso",
+ "pixels": "Píxeles",
+ "charges": "Cargas",
+ "estimatedTime": "Tiempo estimado",
+ "initMessage": "Haz clic en 'Subir imagen' para comenzar",
+ "waitingInit": "Esperando inicialización...",
+ "initializingToken": "🔧 Inicializando generador de token Turnstile...",
+ "tokenReady": "✅ ¡Generador de token listo! Ya puedes empezar a pintar.",
+ "tokenRetryLater": "⚠️ El generador de token volverá a intentarlo cuando sea necesario",
+ "resizeSuccess": "✅ Imagen redimensionada a {width}x{height}",
+ "paintingPaused": "⏸️ Pintado pausado en la posición X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Falló la generación de token. Por favor, inténtalo de nuevo en un momento.",
+ "saveData": "Guardar progreso",
+ "loadData": "Cargar progreso",
+ "saveToFile": "Guardar en archivo",
+ "loadFromFile": "Cargar desde archivo",
+ "dataManager": "Gestor de datos",
+ "autoSaved": "✅ Progreso guardado automáticamente",
+ "dataLoaded": "✅ Progreso cargado con éxito",
+ "fileSaved": "✅ Progreso guardado en archivo con éxito",
+ "fileLoaded": "✅ Progreso cargado desde archivo con éxito",
+ "noSavedData": "❌ No se encontró progreso guardado",
+ "savedDataFound": "✅ ¡Se encontró progreso guardado! ¿Cargar para continuar?",
+ "savedDate": "Guardado el: {date}",
+ "clickLoadToContinue": "Haz clic en 'Cargar progreso' para continuar.",
+ "fileError": "❌ Error al procesar el archivo",
+ "invalidFileFormat": "❌ Formato de archivo inválido",
+ "paintingSpeed": "Velocidad de pintado",
+ "pixelsPerSecond": "píxeles/segundo",
+ "speedSetting": "Velocidad: {speed} píxeles/seg",
+ "settings": "Ajustes",
+ "botSettings": "Ajustes del bot",
+ "close": "Cerrar",
+ "language": "Idioma",
+ "themeSettings": "Ajustes de tema",
+ "themeSettingsDesc": "Elige tu tema de color preferido para la interfaz.",
+ "languageSelectDesc": "Selecciona tu idioma preferido. Los cambios se aplicarán inmediatamente.",
+ "autoCaptcha": "Solucionador automático de CAPTCHA (Turnstile)",
+ "autoCaptchaDesc": "Genera automáticamente tokens de Turnstile usando el generador integrado. Utiliza la automatización del navegador como alternativa si es necesario.",
+ "applySettings": "Aplicar ajustes",
+ "settingsSaved": "✅ ¡Ajustes guardados con éxito!",
+ "speedOn": "Activado",
+ "speedOff": "Desactivado",
+ "cooldownSettings": "Ajustes de tiempo de espera",
+ "waitCharges": "Esperar hasta que las cargas lleguen a",
+ "captchaSolving": "🔑 Generando token de Turnstile...",
+ "captchaFailed": "❌ Falló la generación de token de Turnstile. Probando método alternativo...",
+ "automation": "Automatización",
+ "noChargesThreshold": "⌛ Esperando que las cargas lleguen a {threshold}. Actual: {current}. Siguiente en {time}...",
+ "tokenCapturedSuccess": "¡Token capturado con éxito! Ya puedes iniciar el bot.",
+ "notificationsNotSupported": "Las notificaciones no son compatibles con este navegador.",
+ "chargesReadyNotification": "WPlace — Cargas listas",
+ "chargesReadyMessage": "Cargas listas: {current} / {max}. Umbral: {threshold}.",
+ "testNotificationTitle": "WPlace — Prueba",
+ "testNotificationMessage": "Esta es una notificación de prueba.",
+ "showStats": "Mostrar estadísticas",
+ "compactMode": "Modo compacto",
+ "refreshCharges": "Actualizar cargas",
+ "closeStats": "Cerrar estadísticas",
+ "zoomOut": "Alejar",
+ "zoomIn": "Acercar",
+ "fitToView": "Ajustar a la vista",
+ "actualSize": "Tamaño real (100%)",
+ "panMode": "Desplazamiento (arrastra para mover la vista)",
+ "clearIgnoredPixels": "Limpiar todos los píxeles ignorados",
+ "invertMask": "Invertir máscara",
+ "waitingSetupComplete": "🔄 Esperando que se complete la configuración inicial...",
+ "waitingTokenGenerator": "🔄 Esperando que se inicialice el generador de tokens...",
+ "uploadImageFirst": "Sube una imagen primero para capturar los colores disponibles",
+ "pleaseWaitInitialSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar el progreso.",
+ "pleaseWaitFileSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar desde un archivo.",
+ "errorSavingProgress": "❌ Error al guardar el progreso",
+ "errorLoadingProgress": "❌ Error al cargar el progreso",
+ "fileOperationsAvailable": "📂 ¡Las operaciones de archivo (Cargar/Subir) ya están disponibles!",
+ "tokenGeneratorReady": "🔑 ¡Generador de tokens listo!",
+ "paintingStats": "Estadísticas de pintado",
+ "enablePaintingSpeedLimit": "Activar límite de velocidad de pintado (control de lotes)",
+ "enableNotifications": "Activar notificaciones",
+ "notifyOnChargesThreshold": "Notificar cuando las cargas alcancen el umbral",
+ "onlyWhenNotFocused": "Solo cuando la pestaña no esté en foco",
+ "repeatEvery": "Repetir cada",
+ "minutesPl": "minuto(s)",
+ "grantPermission": "Otorgar permiso",
+ "test": "Probar",
+ "showAllColorsIncluding": "Mostrar todos los colores (incluidos los no disponibles)",
+ "chromaWeight": "Peso de croma",
+ "downloadPreview": "Descargar vista previa",
+ "apply": "Aplicar",
+ "cancel": "Cancelar",
+ "fit": "Ajustar",
+ "hundred": "100%",
+ "clear": "Limpiar",
+ "invert": "Invertir",
+ "reprocessingOverlay": "Reprocesando superposición...",
+ "overlayUpdated": "¡Superposición actualizada!",
+ "notificationsEnabled": "Notificaciones activadas.",
+ "notificationsPermissionDenied": "Permiso de notificaciones denegado.",
+ "overlayEnabled": "Superposición activada.",
+ "overlayDisabled": "Superposición desactivada.",
+ "tokenSourceSet": "Fuente de token establecida en: {source}",
+ "batchModeSet": "Modo por lotes establecido en: {mode}",
+ "randomRange": "Rango aleatorio",
+ "normalFixedSize": "Tamaño fijo normal",
+ "advancedColorSettingsReset": "Ajustes de color avanzados restablecidos.",
+ "shiftRowAltColumn": "Shift = Fila • Alt = Columna",
+ "hideTurnstileBtn": "Ocultar",
+ "turnstileInstructions": "Cloudflare Turnstile — por favor, completa la verificación si se muestra",
+ "uploadImageFirstColors": "Por favor, sube una imagen primero para capturar los colores disponibles",
+ "availableColors": "Colores disponibles ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Modo expandido",
+ "minimize": "Minimizar",
+ "restore": "Restaurar",
+ "hideStats": "Ocultar estadísticas",
+ "paintOptions": "Opciones de pintado",
+ "paintWhitePixels": "Pintar píxeles blancos",
+ "paintTransparentPixels": "Pintar píxeles transparentes"
+}
diff --git a/lang/pt.json b/lang/pt.json
new file mode 100644
index 00000000..ebbc4f6e
--- /dev/null
+++ b/lang/pt.json
@@ -0,0 +1,143 @@
+{
+ "title": "WPlace Auto-Image",
+ "toggleOverlay": "Toggle Overlay",
+ "scanColors": "Escanear Cores",
+ "uploadImage": "Upload da Imagem",
+ "resizeImage": "Redimensionar Imagem",
+ "selectPosition": "Selecionar Posição",
+ "startPainting": "Iniciar Pintura",
+ "stopPainting": "Parar Pintura",
+ "checkingColors": "🔍 Verificando cores disponíveis...",
+ "noColorsFound": "❌ Abra a paleta de cores no site e tente novamente!",
+ "colorsFound": "✅ {count} cores encontradas. Pronto para upload.",
+ "loadingImage": "🖼️ Carregando imagem...",
+ "imageLoaded": "✅ Imagem carregada com {count} pixels válidos",
+ "imageError": "❌ Erro ao carregar imagem",
+ "selectPositionAlert": "Pinte o primeiro pixel на localização onde deseja que a arte comece!",
+ "waitingPosition": "👆 Aguardando você pintar o pixel de referência...",
+ "positionSet": "✅ Posição definida com sucesso!",
+ "positionTimeout": "❌ Tempo esgotado para selecionar posição",
+ "startPaintingMsg": "🎨 Iniciando pintura...",
+ "paintingProgress": "🧱 Progresso: {painted}/{total} pixels...",
+ "noCharges": "⌛ Sem cargas. Aguardando {time}...",
+ "paintingStopped": "⏹️ Pintura interromпида pelo usuário",
+ "paintingComplete": "✅ Pintura concluída! {count} pixels pintados.",
+ "paintingError": "❌ Erro durante a pintura",
+ "missingRequirements": "❌ Carregue uma imagem e selecione uma posição primeiro",
+ "progress": "Progresso",
+ "pixels": "Pixels",
+ "charges": "Cargas",
+ "estimatedTime": "Tempo estimado",
+ "initMessage": "Clique em 'Upload da Imagem' para começar",
+ "waitingInit": "Aguardando inicialização...",
+ "initializingToken": "🔧 Inicializando gerador de tokens Turnstile...",
+ "tokenReady": "✅ Gerador de tokens pronto - você pode começar a pintar!",
+ "tokenRetryLater": "⚠️ Gerador de tokens tentará novamente quando necessário",
+ "resizeSuccess": "✅ Imagem redimensionada для {width}x{height}",
+ "paintingPaused": "⏸️ Pintura pausada na posição X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Falha na geração de token. Tente novamente em alguns instantes.",
+ "saveData": "Salvar Progresso",
+ "loadData": "Carregar Progresso",
+ "saveToFile": "Salvar em Arquivo",
+ "loadFromFile": "Carregar de Arquivo",
+ "dataManager": "Dados",
+ "autoSaved": "✅ Progresso salvo automaticamente",
+ "dataLoaded": "✅ Progresso carregado com sucesso",
+ "fileSaved": "✅ Salvo em arquivo com sucesso",
+ "fileLoaded": "✅ Carregado de arquivo 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.",
+ "fileError": "❌ Erro ao processar arquivo",
+ "invalidFileFormat": "❌ Formato de arquivo inválido",
+ "paintingSpeed": "Velocidade de Pintura",
+ "pixelsPerSecond": "pixels/segundo",
+ "speedSetting": "Velocidade: {speed} pixels/seg",
+ "settings": "Configurações",
+ "botSettings": "Configurações do Bot",
+ "close": "Fechar",
+ "language": "Idioma",
+ "themeSettings": "Configurações de Tema",
+ "themeSettingsDesc": "Escolha seu tema de cores preferido para a interface.",
+ "languageSelectDesc": "Selecione seu idioma preferido. As alterações terão efeito imediatamente.",
+ "autoCaptcha": "Resolvedor de CAPTCHA Automático",
+ "autoCaptchaDesc": "Tenta resolver o CAPTCHA automaticamente simulando a colocação manual de um pixel quando o token expira.",
+ "applySettings": "Aplicar Configurações",
+ "settingsSaved": "✅ Configurações salvas com sucesso!",
+ "speedOn": "Ligado",
+ "speedOff": "Desligado",
+ "cooldownSettings": "Configurações de Cooldown",
+ "waitCharges": "Aguardar até as cargas atingirem",
+ "captchaSolving": "🤖 Tentando resolver o CAPTCHA...",
+ "captchaFailed": "❌ Falha ao resolver CAPTCHA. Pinte um pixel manualmente.",
+ "automation": "Automação", "noChargesThreshold": "⌛ Aguardando cargas atingirem {threshold}. Atual: {current}. Próxima em {time}...",
+ "tokenCapturedSuccess": "Token capturado com sucesso! Você pode iniciar o bot agora.",
+ "notificationsNotSupported": "Notificações não são suportadas neste navegador.",
+ "chargesReadyNotification": "WPlace — Cargas Prontas",
+ "chargesReadyMessage": "Cargas prontas: {current} / {max}. Limite: {threshold}.",
+ "testNotificationTitle": "WPlace — Teste",
+ "testNotificationMessage": "Esta é uma notificação de teste.",
+ "showStats": "Mostrar Estatísticas",
+ "compactMode": "Modo Compacto",
+ "refreshCharges": "Atualizar Cargas",
+ "closeStats": "Fechar Estatísticas",
+ "zoomOut": "Diminuir Zoom",
+ "zoomIn": "Aumentar Zoom",
+ "fitToView": "Ajustar à visualização",
+ "actualSize": "Tamanho real (100%)",
+ "panMode": "Arrastar (arrastar para mover visualização)",
+ "clearIgnoredPixels": "Limpar pixels ignorados",
+ "invertMask": "Inverter máscara",
+ "waitingSetupComplete": "🔄 Aguardando conclusão da configuração inicial...",
+ "waitingTokenGenerator": "🔄 Aguardando inicialização do gerador de tokens...",
+ "uploadImageFirst": "Faça upload de uma imagem primeiro para capturar cores disponíveis",
+ "pleaseWaitInitialSetup": "🔄 Aguarde a conclusão da configuração inicial antes de carregar o progresso.",
+ "pleaseWaitFileSetup": "🔄 Aguarde a conclusão da configuração inicial antes de carregar do arquivo.",
+ "errorSavingProgress": "❌ Erro ao salvar progresso",
+ "errorLoadingProgress": "❌ Erro ao carregar progresso",
+ "fileOperationsAvailable": "📂 Operações de arquivo (Carregar/Upload) agora disponíveis!",
+ "tokenGeneratorReady": "🔑 Gerador de tokens pronto!",
+ "paintingStats": "Estatísticas de Pintura",
+ "enablePaintingSpeedLimit": "Ativar limite de velocidade de pintura (controle de tamanho de lote)",
+ "enableNotifications": "Ativar notificações",
+ "notifyOnChargesThreshold": "Notificar quando as cargas atingirem o limite",
+ "onlyWhenNotFocused": "Apenas quando a aba não está em foco",
+ "repeatEvery": "Repetir a cada",
+ "minutesPl": "minuto(s)",
+ "grantPermission": "Conceder Permissão",
+ "test": "Teste",
+ "showAllColorsIncluding": "Mostrar Todas as Cores (incluindo indisponíveis)",
+ "chromaWeight": "Peso da Saturação",
+ "downloadPreview": "Baixar Pré-visualização",
+ "apply": "Aplicar",
+ "cancel": "Cancelar",
+ "fit": "Ajustar",
+ "hundred": "100%",
+ "clear": "Limpar",
+ "invert": "Inverter",
+ "reprocessingOverlay": "Reprocessando sobreposição...",
+ "overlayUpdated": "Sobreposição atualizada!",
+ "notificationsEnabled": "Notificações ativadas.",
+ "notificationsPermissionDenied": "Permissão de notificações negada.",
+ "overlayEnabled": "Sobreposição ativada.",
+ "overlayDisabled": "Sobreposição desativada.",
+ "tokenSourceSet": "Fonte de token definida para: {source}",
+ "batchModeSet": "Modo de lote definido para: {mode}",
+ "randomRange": "Intervalo Aleatório",
+ "normalFixedSize": "Tamanho Fixo Normal",
+ "advancedColorSettingsReset": "Configurações avançadas de cor redefinidas.",
+ "shiftRowAltColumn": "Shift = Linha • Alt = Coluna",
+ "hideTurnstileBtn": "Ocultar",
+ "turnstileInstructions": "Cloudflare Turnstile — complete a verificação se mostrada",
+ "uploadImageFirstColors": "Faça upload de uma imagem primeiro para capturar cores disponíveis",
+ "availableColors": "Cores Disponíveis ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Modo Expandir",
+ "minimize": "Minimizar",
+ "restore": "Restaurar",
+ "hideStats": "Ocultar Estatísticas",
+ "paintOptions": "Opções de pintura",
+ "paintWhitePixels": "Pintar pixels brancos",
+ "paintTransparentPixels": "Pintar pixels transparentes"
+}
\ No newline at end of file
diff --git a/lang/ru.json b/lang/ru.json
new file mode 100644
index 00000000..1bb8b162
--- /dev/null
+++ b/lang/ru.json
@@ -0,0 +1,143 @@
+{
+ "title": "WPlace Авто-Изображение",
+ "toggleOverlay": "Toggle Overlay",
+ "scanColors": "Сканировать цвета",
+ "uploadImage": "Загрузить изображение",
+ "resizeImage": "Изменить размер изображения",
+ "selectPosition": "Выбрать позицию",
+ "startPainting": "Начать рисование",
+ "stopPainting": "Остановить рисование",
+ "checkingColors": "🔍 Проверка доступных цветов...",
+ "noColorsFound": "❌ Откройте палитру цветов на сайте и попробуйте снова!",
+ "colorsFound": "✅ Найдено доступных цветов: {count}. Готово к загрузке.",
+ "loadingImage": "🖼️ Загрузка изображения...",
+ "imageLoaded": "✅ Изображение загружено, валидных пикселей: {count}",
+ "imageError": "❌ Ошибка при загрузке изображения",
+ "selectPositionAlert": "Нарисуйте первый пиксель в месте, откуда начнётся рисунок!",
+ "waitingPosition": "👆 Ожидание, пока вы нарисуете опорный пиксель...",
+ "positionSet": "✅ Позиция успешно установлена!",
+ "positionTimeout": "❌ Время ожидания выбора позиции истекло",
+ "startPaintingMsg": "🎨 Начинаем рисование...",
+ "paintingProgress": "🧱 Прогресс: {painted}/{total} пикселей...",
+ "noCharges": "⌛ Нет зарядов. Ожидание {time}...",
+ "paintingStopped": "⏹️ Рисование остановлено пользователем",
+ "paintingComplete": "✅ Рисование завершено! Нарисовано пикселей: {count}.",
+ "paintingError": "❌ Ошибка во время рисования",
+ "missingRequirements": "❌ Сначала загрузите изображение и выберите позицию",
+ "progress": "Прогресс",
+ "pixels": "Пиксели",
+ "charges": "Заряды",
+ "estimatedTime": "Примерное время",
+ "initMessage": "Нажмите 'Загрузить изображение', чтобы начать",
+ "waitingInit": "Ожидание инициализации...",
+ "initializingToken": "🔧 Инициализация генератора Turnstile токенов...",
+ "tokenReady": "✅ Генератор токенов готов - можете начинать рисование!",
+ "tokenRetryLater": "⚠️ Генератор токенов повторит попытку при необходимости",
+ "resizeSuccess": "✅ Изображение изменено до {width}x{height}",
+ "paintingPaused": "⏸️ Рисование приостановлено на позиции X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Генерация токена не удалась. Пожалуйста, попробуйте через некоторое время.",
+ "saveData": "Сохранить прогресс",
+ "loadData": "Загрузить прогресс",
+ "saveToFile": "Сохранить в файл",
+ "loadFromFile": "Загрузить из файла",
+ "dataManager": "Менеджер данных",
+ "autoSaved": "✅ Прогресс сохранён автоматически",
+ "dataLoaded": "✅ Прогресс успешно загружен",
+ "fileSaved": "✅ Прогресс успешно сохранён в файл",
+ "fileLoaded": "✅ Прогресс успешно загружен из файла",
+ "noSavedData": "❌ Сохранённый прогресс не найден",
+ "savedDataFound": "✅ Найден сохранённый прогресс! Загрузить, чтобы продолжить?",
+ "savedDate": "Сохранено: {date}",
+ "clickLoadToContinue": "Нажмите 'Загрузить прогресс', чтобы продолжить.",
+ "fileError": "❌ Ошибка при обработке файла",
+ "invalidFileFormat": "❌ Неверный формат файла",
+ "paintingSpeed": "Скорость рисования",
+ "pixelsPerSecond": "пикселей/сек",
+ "speedSetting": "Скорость: {speed} пикс./сек",
+ "settings": "Настройки",
+ "botSettings": "Настройки бота",
+ "close": "Закрыть",
+ "language": "Язык",
+ "themeSettings": "Настройки темы",
+ "themeSettingsDesc": "Выберите предпочтительную цветовую тему интерфейса.",
+ "languageSelectDesc": "Выберите предпочтительный язык. Изменения вступят в силу немедленно.",
+ "autoCaptcha": "Авто-решение CAPTCHA (Turnstile)",
+ "autoCaptchaDesc": "Автоматически генерирует Turnstile токены используя встроенный генератор. Возвращается к автоматизации браузера при необходимости.",
+ "applySettings": "Применить настройки",
+ "settingsSaved": "✅ Настройки успешно сохранены!",
+ "speedOn": "Вкл",
+ "speedOff": "Выкл",
+ "cooldownSettings": "Настройки перезарядки",
+ "waitCharges": "Ждать до накопления зарядов",
+ "captchaSolving": "🔑 Генерирую Turnstile токен...",
+ "captchaFailed": "❌ Не удалось сгенерировать Turnstile токен. Пробую резервный метод...",
+ "automation": "Автоматизация", "noChargesThreshold": "⌛ Ожидание зарядов до {threshold}. Сейчас {current}. Следующий через {time}...",
+ "tokenCapturedSuccess": "Токен успешно захвачен! Теперь можно запускать бота.",
+ "notificationsNotSupported": "Уведомления не поддерживаются в этом браузере.",
+ "chargesReadyNotification": "WPlace — Заряды готовы",
+ "chargesReadyMessage": "Заряды готовы: {current} / {max}. Порог: {threshold}.",
+ "testNotificationTitle": "WPlace — Тест",
+ "testNotificationMessage": "Это тестовое уведомление.",
+ "showStats": "Показать статистику",
+ "compactMode": "Компактный режим",
+ "refreshCharges": "Обновить заряды",
+ "closeStats": "Закрыть статистику",
+ "zoomOut": "Уменьшить",
+ "zoomIn": "Увеличить",
+ "fitToView": "По размеру экрана",
+ "actualSize": "Реальный размер (100%)",
+ "panMode": "Перемещение (перетаскивание для движения)",
+ "clearIgnoredPixels": "Очистить игнорируемые пиксели",
+ "invertMask": "Инвертировать маску",
+ "waitingSetupComplete": "🔄 Ждём завершения начальной настройки...",
+ "waitingTokenGenerator": "🔄 Ждём инициализации генератора токенов...",
+ "uploadImageFirst": "Сначала загрузите изображение для захвата доступных цветов",
+ "pleaseWaitInitialSetup": "🔄 Пожалуйста, подождите завершения начальной настройки перед загрузкой прогресса.",
+ "pleaseWaitFileSetup": "🔄 Пожалуйста, подождите завершения начальной настройки перед загрузкой из файла.",
+ "errorSavingProgress": "❌ Ошибка сохранения прогресса",
+ "errorLoadingProgress": "❌ Ошибка загрузки прогресса",
+ "fileOperationsAvailable": "📂 Файловые операции (Загрузка/Выгрузка) теперь доступны!",
+ "tokenGeneratorReady": "🔑 Генератор токенов готов!",
+ "paintingStats": "Статистика рисования",
+ "enablePaintingSpeedLimit": "Включить ограничение скорости рисования (контроль размера пакета)",
+ "enableNotifications": "Включить уведомления",
+ "notifyOnChargesThreshold": "Уведомлять при достижении порога зарядов",
+ "onlyWhenNotFocused": "Только когда вкладка не в фокусе",
+ "repeatEvery": "Повторять каждые",
+ "minutesPl": "минут(а/ы)",
+ "grantPermission": "Дать разрешение",
+ "test": "Тест",
+ "showAllColorsIncluding": "Показать все цвета (включая недоступные)",
+ "chromaWeight": "Вес цветности",
+ "downloadPreview": "Скачать превью",
+ "apply": "Применить",
+ "cancel": "Отменить",
+ "fit": "По размеру",
+ "hundred": "100%",
+ "clear": "Очистить",
+ "invert": "Инвертировать",
+ "reprocessingOverlay": "Переобработка оверлея...",
+ "overlayUpdated": "Оверлей обновлён!",
+ "notificationsEnabled": "Уведомления включены.",
+ "notificationsPermissionDenied": "В разрешении на уведомления отказано.",
+ "overlayEnabled": "Оверлей включён.",
+ "overlayDisabled": "Оверлей отключён.",
+ "tokenSourceSet": "Источник токенов установлен: {source}",
+ "batchModeSet": "Режим пакета установлен: {mode}",
+ "randomRange": "Случайный диапазон",
+ "normalFixedSize": "Нормальный фиксированный размер",
+ "advancedColorSettingsReset": "Продвинутые настройки цвета сброшены.",
+ "shiftRowAltColumn": "Shift = Строка • Alt = Столбец",
+ "hideTurnstileBtn": "Скрыть",
+ "turnstileInstructions": "Cloudflare Turnstile — пожалуйста, завершите проверку, если она показана",
+ "uploadImageFirstColors": "Пожалуйста, сначала загрузите изображение для захвата доступных цветов",
+ "availableColors": "Доступные цвета ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Режим расширения",
+ "minimize": "Свернуть",
+ "restore": "Восстановить",
+ "hideStats": "Скрыть статистику",
+ "paintOptions": "Параметры рисования",
+ "paintWhitePixels": "Рисовать белые пиксели",
+ "paintTransparentPixels": "Рисовать прозрачные пиксели"
+}
\ No newline at end of file
diff --git a/lang/tr.json b/lang/tr.json
new file mode 100644
index 00000000..3a12640e
--- /dev/null
+++ b/lang/tr.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Otomatik-Resim",
+ "toggleOverlay": "Katmanı Aç/Kapat",
+ "scanColors": "Renkleri Tara",
+ "uploadImage": "Resim Yükle",
+ "resizeImage": "Resmi Yeniden Boyutlandır",
+ "selectPosition": "Konum Seç",
+ "startPainting": "Boyamayı Başlat",
+ "stopPainting": "Boyamayı Durdur",
+ "checkingColors": "🔍 Uygun renkler kontrol ediliyor...",
+ "noColorsFound": "❌ Sitede renk paletini açın ve tekrar deneyin!",
+ "colorsFound": "✅ {count} uygun renk bulundu. Yüklemeye hazır.",
+ "loadingImage": "🖼️ Resim yükleniyor...",
+ "imageLoaded": "✅ Resim {count} geçerli piksel ile yüklendi",
+ "imageError": "❌ Resim yüklenirken hata oluştu",
+ "selectPositionAlert": "Sanatı başlatmak istediğiniz ilk pikseli boyayın!",
+ "waitingPosition": "👆 Referans pikseli boyamanız bekleniyor...",
+ "positionSet": "✅ Konum başarıyla ayarlandı!",
+ "positionTimeout": "❌ Konum seçme süresi doldu",
+ "startPaintingMsg": "🎨 Boyama başlatılıyor...",
+ "paintingProgress": "🧱 İlerleme: {painted}/{total} piksel...",
+ "noCharges": "⌛ Yeterli hak yok. Bekleniyor {time}...",
+ "paintingStopped": "⏹️ Boyama kullanıcı tarafından durduruldu",
+ "paintingComplete": "✅ Boyama tamamlandı! {count} piksel boyandı.",
+ "paintingError": "❌ Boyama sırasında hata oluştu",
+ "missingRequirements": "❌ Önce resim yükleyip konum seçmelisiniz",
+ "progress": "İlerleme",
+ "pixels": "Pikseller",
+ "charges": "Haklar",
+ "estimatedTime": "Tahmini süre",
+ "initMessage": "Başlamak için 'Resim Yükle'ye tıklayın",
+ "waitingInit": "Başlatma bekleniyor...",
+ "initializingToken": "🔧 Turnstile token üreticisi başlatılıyor...",
+ "tokenReady": "✅ Token üreteci hazır - artık boyamaya başlayabilirsiniz!",
+ "tokenRetryLater": "⚠️ Token üreteci gerektiğinde yeniden deneyecek",
+ "resizeSuccess": "✅ Resim {width}x{height} boyutuna yeniden boyutlandırıldı",
+ "paintingPaused": "⏸️ Boyama duraklatıldı, Konum X: {x}, Y: {y}",
+ "captchaNeeded": "❗ CAPTCHA gerekli. Devam etmek için bir pikseli manuel olarak boyayın.",
+ "saveData": "İlerlemeyi Kaydet",
+ "loadData": "İlerlemeyi Yükle",
+ "saveToFile": "Dosyaya Kaydet",
+ "loadFromFile": "Dosyadan Yükle",
+ "dataManager": "Veri Yöneticisi",
+ "autoSaved": "✅ İlerleme otomatik olarak kaydedildi",
+ "dataLoaded": "✅ İlerleme başarıyla yüklendi",
+ "fileSaved": "✅ İlerleme dosyaya başarıyla kaydedildi",
+ "fileLoaded": "✅ İlerleme dosyadan başarıyla yüklendi",
+ "noSavedData": "❌ Kayıtlı ilerleme bulunamadı",
+ "savedDataFound": "✅ Kayıtlı ilerleme bulundu! Devam etmek için yükleyin.",
+ "savedDate": "Kaydedilme tarihi: {date}",
+ "clickLoadToContinue": "Devam etmek için 'İlerlemeyi Yükle'ye tıklayın.",
+ "fileError": "❌ Dosya işlenirken hata oluştu",
+ "invalidFileFormat": "❌ Geçersiz dosya formatı",
+ "paintingSpeed": "Boyama Hızı",
+ "pixelsPerSecond": "piksel/saniye",
+ "speedSetting": "Hız: {speed} piksel/sn",
+ "settings": "Ayarlar",
+ "botSettings": "Bot Ayarları",
+ "close": "Kapat",
+ "language": "Dil",
+ "themeSettings": "Tema Ayarları",
+ "themeSettingsDesc": "Arayüz için tercih ettiğiniz renk temasını seçin.",
+ "languageSelectDesc": "Tercih ettiğiniz dili seçin. Değişiklikler hemen uygulanacaktır.",
+ "autoCaptcha": "Oto-CAPTCHA Çözücü",
+ "autoCaptchaDesc": "CAPTCHA süresi dolduğunda manuel piksel yerleştirmeyi taklit ederek otomatik çözmeyi dener.",
+ "applySettings": "Ayarları Uygula",
+ "settingsSaved": "✅ Ayarlar başarıyla kaydedildi!",
+ "speedOn": "Açık",
+ "speedOff": "Kapalı",
+ "cooldownSettings": "Bekleme Süresi Ayarları",
+ "waitCharges": "Haklar şu seviyeye ulaşana kadar bekle",
+ "captchaSolving": "🤖 CAPTCHA çözülmeye çalışılıyor...",
+ "captchaFailed": "❌ Oto-CAPTCHA başarısız oldu. Bir pikseli manuel boyayın.",
+ "automation": "Otomasyon",
+ "noChargesThreshold": "⌛ Hakların {threshold} seviyesine ulaşması bekleniyor. Şu anda {current}. Sonraki {time} içinde...",
+ "tokenCapturedSuccess": "Token başarıyla yakalandı! Şimdi botu başlatabilirsiniz.",
+ "notificationsNotSupported": "Bu tarayıcıda bildirimler desteklenmiyor.",
+ "chargesReadyNotification": "WPlace — Haklar Hazır",
+ "chargesReadyMessage": "Haklar hazır: {current} / {max}. Eşik: {threshold}.",
+ "testNotificationTitle": "WPlace — Test",
+ "testNotificationMessage": "Bu bir test bildirimidir.",
+ "showStats": "İstatistikleri Göster",
+ "compactMode": "Kompakt Mod",
+ "refreshCharges": "Hakları Yenile",
+ "closeStats": "İstatistikleri Kapat",
+ "zoomOut": "Uzaklaştır",
+ "zoomIn": "Yakınlaştır",
+ "fitToView": "Görünüme sığdır",
+ "actualSize": "Gerçek boyut (100%)",
+ "panMode": "Kaydır (görünümü hareket ettirmek için sürükle)",
+ "clearIgnoredPixels": "Görmezden gelinen tüm pikselleri temizle",
+ "invertMask": "Maskeyi ters çevir",
+ "waitingSetupComplete": "🔄 İlk kurulumun tamamlanması bekleniyor...",
+ "waitingTokenGenerator": "🔄 Token üretecinin başlatılması bekleniyor...",
+ "uploadImageFirst": "Uygun renkleri yakalamak için önce bir resim yükleyin",
+ "pleaseWaitInitialSetup": "🔄 İlerlemeyi yüklemeden önce lütfen ilk kurulumun tamamlanmasını bekleyin.",
+ "pleaseWaitFileSetup": "🔄 Dosyadan yüklemeden önce lütfen ilk kurulumun tamamlanmasını bekleyin.",
+ "errorSavingProgress": "❌ İlerlemeyi kaydetme hatası",
+ "errorLoadingProgress": "❌ İlerlemeyi yükleme hatası",
+ "fileOperationsAvailable": "📂 Dosya işlemleri (Yükle/Karşıya Yükle) artık mevcut!",
+ "tokenGeneratorReady": "🔑 Token üreteci hazır!",
+ "paintingStats": "Boyama İstatistikleri",
+ "enablePaintingSpeedLimit": "Boyama hız limitini etkinleştir (batch boyut kontrolü)",
+ "enableNotifications": "Bildirimleri etkinleştir",
+ "notifyOnChargesThreshold": "Haklar eşiğe ulaştığında bildir",
+ "onlyWhenNotFocused": "Sadece sekme odaklanmadığında",
+ "repeatEvery": "Her tekrarla",
+ "minutesPl": "dakika",
+ "grantPermission": "İzin Ver",
+ "test": "Test",
+ "showAllColorsIncluding": "Tüm Renkleri Göster (kullanılamayanlar dahil)",
+ "chromaWeight": "Renk Doygunluğu Ağırlığı",
+ "downloadPreview": "Önizlemeyi İndir",
+ "apply": "Uygula",
+ "cancel": "İptal",
+ "fit": "Sığdır",
+ "hundred": "100%",
+ "clear": "Temizle",
+ "invert": "Ters çevir",
+ "reprocessingOverlay": "Katman yeniden işleniyor...",
+ "overlayUpdated": "Katman güncellendi!",
+ "notificationsEnabled": "Bildirimler etkinleştirildi.",
+ "notificationsPermissionDenied": "Bildirim izni reddedildi.",
+ "overlayEnabled": "Katman etkinleştirildi.",
+ "overlayDisabled": "Katman devre dışı bırakıldı.",
+ "tokenSourceSet": "Token kaynağı şuna ayarlandı: {source}",
+ "batchModeSet": "Batch modu şuna ayarlandı: {mode}",
+ "randomRange": "Rastgele Aralık",
+ "normalFixedSize": "Normal Sabit Boyut",
+ "advancedColorSettingsReset": "Gelişmiş renk ayarları sıfırlandı.",
+ "shiftRowAltColumn": "Shift = Satır • Alt = Sütun",
+ "hideTurnstileBtn": "Gizle",
+ "turnstileInstructions": "Cloudflare Turnstile — gösterilirse lütfen kontrolü tamamlayın",
+ "uploadImageFirstColors": "Uygun renkleri yakalamak için lütfen önce bir resim yükleyin",
+ "availableColors": "Uygun Renkler ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Genişletme Modu",
+ "minimize": "Küçült",
+ "restore": "Geri Yükle",
+ "hideStats": "İstatistikleri Gizle",
+ "paintOptions": "Boya Seçenekleri",
+ "paintWhitePixels": "Beyaz pikselleri boya",
+ "paintTransparentPixels": "Şeffaf pikselleri boya"
+}
diff --git a/lang/uk.json b/lang/uk.json
new file mode 100644
index 00000000..34171c58
--- /dev/null
+++ b/lang/uk.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Авто-Зображення",
+ "toggleOverlay": "Перемкнути оверлей",
+ "scanColors": "Сканувати кольори",
+ "uploadImage": "Завантажити зображення",
+ "resizeImage": "Змінити розмір зображення",
+ "selectPosition": "Вибрати позицію",
+ "startPainting": "Почати малювання",
+ "stopPainting": "Зупинити малювання",
+ "checkingColors": "🔍 Перевірка доступних кольорів...",
+ "noColorsFound": "❌ Відкрий палітру кольорів на сайті та спробуй ще раз!",
+ "colorsFound": "✅ Знайдено {count} доступних кольорів. Готово до завантаження.",
+ "loadingImage": "🖼️ Завантаження зображення...",
+ "imageLoaded": "✅ Зображення завантажено. Валідних пікселів: {count}",
+ "imageError": "❌ Помилка завантаження зображення",
+ "selectPositionAlert": "Намалюй перший піксель у місці, де має починатися арт!",
+ "waitingPosition": "👆 Очікування на малювання референсного пікселя...",
+ "positionSet": "✅ Позицію успішно встановлено!",
+ "positionTimeout": "❌ Час вибору позиції вичерпано",
+ "startPaintingMsg": "🎨 Початок малювання...",
+ "paintingProgress": "🧱 Прогрес: {painted}/{total} пікселів...",
+ "noCharges": "⌛ Немає зарядів. Очікування {time}...",
+ "paintingStopped": "⏹️ Малювання зупинено користувачем",
+ "paintingComplete": "✅ Малювання завершено! Намальовано {count} пікселів.",
+ "paintingError": "❌ Помилка під час малювання",
+ "missingRequirements": "❌ Спершу завантаж зображення та вибери позицію",
+ "progress": "Прогрес",
+ "pixels": "Пікселі",
+ "charges": "Заряди",
+ "estimatedTime": "Орієнтовний час",
+ "initMessage": "Натисни 'Завантажити зображення', щоб почати",
+ "waitingInit": "Очікування ініціалізації...",
+ "initializingToken": "🔧 Ініціалізація генератора токенів Turnstile...",
+ "tokenReady": "✅ Генератор токенів готовий – можна починати малювання!",
+ "tokenRetryLater": "⚠️ Генератор токенів повторить спробу за потреби",
+ "resizeSuccess": "✅ Зображення змінено до {width}x{height}",
+ "paintingPaused": "⏸️ Малювання призупинено на позиції X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Не вдалося згенерувати токен. Спробуй трохи пізніше.",
+ "saveData": "Зберегти прогрес",
+ "loadData": "Завантажити прогрес",
+ "saveToFile": "Зберегти у файл",
+ "loadFromFile": "Завантажити з файлу",
+ "dataManager": "Менеджер даних",
+ "autoSaved": "✅ Прогрес збережено автоматично",
+ "dataLoaded": "✅ Прогрес успішно завантажено",
+ "fileSaved": "✅ Прогрес успішно збережено у файл",
+ "fileLoaded": "✅ Прогрес успішно завантажено з файлу",
+ "noSavedData": "❌ Не знайдено збереженого прогресу",
+ "savedDataFound": "✅ Знайдено збережений прогрес! Завантажити, щоб продовжити?",
+ "savedDate": "Збережено: {date}",
+ "clickLoadToContinue": "Натисни 'Завантажити прогрес', щоб продовжити.",
+ "fileError": "❌ Помилка обробки файлу",
+ "invalidFileFormat": "❌ Невірний формат файлу",
+ "paintingSpeed": "Швидкість малювання",
+ "pixelsPerSecond": "пікселів/секунда",
+ "speedSetting": "Швидкість: {speed} пікселів/сек",
+ "settings": "Налаштування",
+ "botSettings": "Налаштування бота",
+ "close": "Закрити",
+ "language": "Мова",
+ "themeSettings": "Налаштування теми",
+ "themeSettingsDesc": "Вибери бажану колірну тему для інтерфейсу.",
+ "languageSelectDesc": "Вибери бажану мову. Зміни набудуть чинності одразу.",
+ "autoCaptcha": "Авто-CAPTCHA (Turnstile)",
+ "autoCaptchaDesc": "Автоматично генерує токени Turnstile за допомогою вбудованого генератора. Використовує автоматизацію браузера у разі потреби.",
+ "applySettings": "Застосувати налаштування",
+ "settingsSaved": "✅ Налаштування успішно збережено!",
+ "speedOn": "Увімк",
+ "speedOff": "Вимк",
+ "cooldownSettings": "Налаштування відновлення",
+ "waitCharges": "Очікувати, доки кількість зарядів досягне",
+ "captchaSolving": "🔑 Генерація токена Turnstile...",
+ "captchaFailed": "❌ Не вдалося згенерувати токен Turnstile. Використовую запасний метод...",
+ "automation": "Автоматизація",
+ "noChargesThreshold": "⌛ Очікування, доки заряди досягнуть {threshold}. Зараз {current}. Наступне через {time}...",
+ "tokenCapturedSuccess": "Токен успішно захоплено! Можеш запускати бота.",
+ "notificationsNotSupported": "Сповіщення не підтримуються в цьому браузері.",
+ "chargesReadyNotification": "WPlace — Заряди готові",
+ "chargesReadyMessage": "Заряди готові: {current} / {max}. Поріг: {threshold}.",
+ "testNotificationTitle": "WPlace — Тест",
+ "testNotificationMessage": "Це тестове сповіщення.",
+ "showStats": "Показати статистику",
+ "compactMode": "Компактний режим",
+ "refreshCharges": "Оновити заряди",
+ "closeStats": "Закрити статистику",
+ "zoomOut": "Зменшити",
+ "zoomIn": "Збільшити",
+ "fitToView": "Підігнати під вікно",
+ "actualSize": "Реальний розмір (100%)",
+ "panMode": "Переміщення (перетягни для руху)",
+ "clearIgnoredPixels": "Очистити всі ігноровані пікселі",
+ "invertMask": "Інвертувати маску",
+ "waitingSetupComplete": "🔄 Очікування завершення початкового налаштування...",
+ "waitingTokenGenerator": "🔄 Очікування ініціалізації генератора токенів...",
+ "uploadImageFirst": "Спершу завантаж зображення для захоплення доступних кольорів",
+ "pleaseWaitInitialSetup": "🔄 Будь ласка, почекай завершення початкового налаштування перед завантаженням прогресу.",
+ "pleaseWaitFileSetup": "🔄 Будь ласка, почекай завершення початкового налаштування перед завантаженням з файлу.",
+ "errorSavingProgress": "❌ Помилка збереження прогресу",
+ "errorLoadingProgress": "❌ Помилка завантаження прогресу",
+ "fileOperationsAvailable": "📂 Файлові операції (Завантаження/Вивантаження) тепер доступні!",
+ "tokenGeneratorReady": "🔑 Генератор токенів готовий!",
+ "paintingStats": "Статистика малювання",
+ "enablePaintingSpeedLimit": "Увімкнути обмеження швидкості малювання (контроль розміру пакета)",
+ "enableNotifications": "Увімкнути уведомлення",
+ "notifyOnChargesThreshold": "Сповіщати при досягненні порогу зарядів",
+ "onlyWhenNotFocused": "Лише коли вкладка не в фокусі",
+ "repeatEvery": "Повторювати кожні",
+ "minutesPl": "хвилин",
+ "grantPermission": "Надати дозвіл",
+ "test": "Тест",
+ "showAllColorsIncluding": "Показати всі кольори (включаючи недоступні)",
+ "chromaWeight": "Вага насиченості",
+ "downloadPreview": "Завантажити попередній перегляд",
+ "apply": "Застосувати",
+ "cancel": "Скасувати",
+ "fit": "Підігнати",
+ "hundred": "100%",
+ "clear": "Очистити",
+ "invert": "Інвертувати",
+ "reprocessingOverlay": "Переобробка оверлея...",
+ "overlayUpdated": "Оверлей оновлено!",
+ "notificationsEnabled": "Уведомлення увімкнено.",
+ "notificationsPermissionDenied": "Дозвіл на уведомлення відхилено.",
+ "overlayEnabled": "Оверлей увімкнено.",
+ "overlayDisabled": "Оверлей вимкнено.",
+ "tokenSourceSet": "Джерело токенів встановлено: {source}",
+ "batchModeSet": "Пакетний режим встановлено: {mode}",
+ "randomRange": "Випадковий діапазон",
+ "normalFixedSize": "Звичайний фіксований розмір",
+ "advancedColorSettingsReset": "Просунуті налаштування кольорів скинуто.",
+ "shiftRowAltColumn": "Shift = Рядок • Alt = Стовпець",
+ "hideTurnstileBtn": "Сховати",
+ "turnstileInstructions": "Cloudflare Turnstile — будь ласка, заверши перевірку, якщо вона показана",
+ "uploadImageFirstColors": "Будь ласка, спершу завантаж зображення для захоплення доступних кольорів",
+ "availableColors": "Доступні кольори ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Режим розширення",
+ "minimize": "Мінімізувати",
+ "restore": "Відновити",
+ "hideStats": "Приховати статистику",
+ "paintOptions": "Параметри малювання",
+ "paintWhitePixels": "Малювати білі пікселі",
+ "paintTransparentPixels": "Малювати прозорі пікселі"
+}
diff --git a/lang/vi.json b/lang/vi.json
new file mode 100644
index 00000000..ea79866f
--- /dev/null
+++ b/lang/vi.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace Auto-Image",
+ "toggleOverlay": "Bật/tắt lớp phủ",
+ "scanColors": "Quét màu",
+ "uploadImage": "Tải lên hình ảnh",
+ "resizeImage": "Thay đổi kích thước",
+ "selectPosition": "Chọn vị trí",
+ "startPainting": "Bắt đầu vẽ",
+ "stopPainting": "Dừng vẽ",
+ "checkingColors": "🔍 Đang kiểm tra màu sắc có sẵn...",
+ "noColorsFound": "❌ Hãy mở bảng màu trên trang web và thử lại!",
+ "colorsFound": "✅ Tìm thấy {count} màu. Sẵn sàng để tải lên.",
+ "loadingImage": "🖼️ Đang tải hình ảnh...",
+ "imageLoaded": "✅ Đã tải hình ảnh với {count} pixel hợp lệ",
+ "imageError": "❌ Lỗi khi tải hình ảnh",
+ "selectPositionAlert": "Vẽ pixel đầu tiên tại vị trí bạn muốn tác phẩm nghệ thuật bắt đầu!",
+ "waitingPosition": "👆 Đang chờ bạn vẽ pixel tham chiếu...",
+ "positionSet": "✅ Đã đặt vị trí thành công!",
+ "positionTimeout": "❌ Hết thời gian chọn vị trí",
+ "startPaintingMsg": "🎨 Bắt đầu vẽ...",
+ "paintingProgress": "🧱 Tiến trình: {painted}/{total} pixel...",
+ "noCharges": "⌛ Không có điện tích. Đang chờ {time}...",
+ "paintingStopped": "⏹️ Người dùng đã dừng vẽ",
+ "paintingComplete": "✅ Hoàn thành vẽ! Đã vẽ {count} pixel.",
+ "paintingError": "❌ Lỗi trong quá trình vẽ",
+ "missingRequirements": "❌ Hãy tải lên hình ảnh và chọn vị trí trước",
+ "progress": "Tiến trình",
+ "pixels": "Pixel",
+ "charges": "Điện tích",
+ "estimatedTime": "Thời gian ước tính",
+ "initMessage": "Nhấp 'Tải lên hình ảnh' để bắt đầu",
+ "waitingInit": "Đang chờ khởi tạo...",
+ "initializingToken": "🔧 Đang khởi tạo bộ tạo token Turnstile...",
+ "tokenReady": "✅ Bộ tạo token đã sẵn sàng - bạn có thể bắt đầu vẽ!",
+ "tokenRetryLater": "⚠️ Bộ tạo token sẽ thử lại khi cần thiết",
+ "resizeSuccess": "✅ Đã thay đổi kích thước hình ảnh thành {width}x{height}",
+ "paintingPaused": "⏸️ Tạm dừng vẽ tại vị trí X: {x}, Y: {y}",
+ "captchaNeeded": "❗ Tạo token thất bại. Vui lòng thử lại sau.",
+ "saveData": "Lưu tiến trình",
+ "loadData": "Tải tiến trình",
+ "saveToFile": "Lưu vào tệp",
+ "loadFromFile": "Tải từ tệp",
+ "dataManager": "Dữ liệu",
+ "autoSaved": "✅ Đã tự động lưu tiến trình",
+ "dataLoaded": "✅ Đã tải tiến trình thành công",
+ "fileSaved": "✅ Đã lưu vào tệp thành công",
+ "fileLoaded": "✅ Đã tải từ tệp thành công",
+ "noSavedData": "❌ Không tìm thấy tiến trình đã lưu",
+ "savedDataFound": "✅ Tìm thấy tiến trình đã lưu! Tải để tiếp tục?",
+ "savedDate": "Đã lưu vào: {date}",
+ "clickLoadToContinue": "Nhấp 'Tải tiến trình' để tiếp tục.",
+ "fileError": "❌ Lỗi khi xử lý tệp",
+ "invalidFileFormat": "❌ Định dạng tệp không hợp lệ",
+ "paintingSpeed": "Tốc độ vẽ",
+ "pixelsPerSecond": "pixel/giây",
+ "speedSetting": "Tốc độ: {speed} pixel/giây",
+ "settings": "Cài đặt",
+ "botSettings": "Cài đặt Bot",
+ "close": "Đóng",
+ "language": "Ngôn ngữ",
+ "themeSettings": "Cài đặt Giao diện",
+ "themeSettingsDesc": "Chọn chủ đề màu sắc yêu thích cho giao diện.",
+ "languageSelectDesc": "Chọn ngôn ngữ ưa thích. Thay đổi sẽ có hiệu lực ngay lập tức.",
+ "autoCaptcha": "Tự động giải CAPTCHA",
+ "autoCaptchaDesc": "Tự động cố gắng giải CAPTCHA bằng cách mô phỏng việc đặt pixel thủ công khi token hết hạn.",
+ "applySettings": "Áp dụng cài đặt",
+ "settingsSaved": "✅ Đã lưu cài đặt thành công!",
+ "speedOn": "Bật",
+ "speedOff": "Tắt",
+ "cooldownSettings": "Cài đặt thời gian chờ",
+ "waitCharges": "Chờ cho đến khi số lần sạc đạt",
+ "captchaSolving": "🤖 Đang cố gắng giải CAPTCHA...",
+ "captchaFailed": "❌ Giải CAPTCHA tự động thất bại. Vui lòng vẽ một pixel thủ công.",
+ "automation": "Tự động hóa",
+ "noChargesThreshold": "⌛ Đang chờ số lần sạc đạt {threshold}. Hiện tại {current}. Lần tiếp theo trong {time}...",
+ "tokenCapturedSuccess": "Đã bắt token thành công! Bạn có thể khởi động bot ngay.",
+ "notificationsNotSupported": "Thông báo không được hỗ trợ trong trình duyệt này.",
+ "chargesReadyNotification": "WPlace — Điện tích sẵn sàng",
+ "chargesReadyMessage": "Điện tích sẵn sàng: {current} / {max}. Ngưỡng: {threshold}.",
+ "testNotificationTitle": "WPlace — Thử nghiệm",
+ "testNotificationMessage": "Đây là thông báo thử nghiệm.",
+ "showStats": "Hiển thị thống kê",
+ "compactMode": "Chế độ gọn",
+ "refreshCharges": "Làm mới điện tích",
+ "closeStats": "Đóng thống kê",
+ "zoomOut": "Thu nhỏ",
+ "zoomIn": "Phóng to",
+ "fitToView": "Vừa màn hình",
+ "actualSize": "Kích thước thực (100%)",
+ "panMode": "Di chuyển (kéo để di chuyển)",
+ "clearIgnoredPixels": "Xóa tất cả pixel bị bỏ qua",
+ "invertMask": "Đảo ngược mặt nạ",
+ "waitingSetupComplete": "🔄 Đang chờ thiết lập ban đầu hoàn tất...",
+ "waitingTokenGenerator": "🔄 Đang chờ bộ tạo token khởi tạo...",
+ "uploadImageFirst": "Tải lên hình ảnh trước để bắt màu có sẵn",
+ "pleaseWaitInitialSetup": "🔄 Vui lòng đợi thiết lập ban đầu hoàn tất trước khi tải tiến trình.",
+ "pleaseWaitFileSetup": "🔄 Vui lòng đợi thiết lập ban đầu hoàn tất trước khi tải từ tệp.",
+ "errorSavingProgress": "❌ Lỗi khi lưu tiến trình",
+ "errorLoadingProgress": "❌ Lỗi khi tải tiến trình",
+ "fileOperationsAvailable": "📂 Các thao tác tệp (Tải/Upload) đã có sẵn!",
+ "tokenGeneratorReady": "🔑 Bộ tạo token đã sẵn sàng!",
+ "paintingStats": "Thống kê vẽ",
+ "enablePaintingSpeedLimit": "Bật giới hạn tốc độ vẽ (kiểm soát kích thước lô)",
+ "enableNotifications": "Bật thông báo",
+ "notifyOnChargesThreshold": "Thông báo khi điện tích đạt ngưỡng",
+ "onlyWhenNotFocused": "Chỉ khi tab không được chọn",
+ "repeatEvery": "Lặp lại mỗi",
+ "minutesPl": "phút",
+ "grantPermission": "Cấp quyền",
+ "test": "Thử nghiệm",
+ "showAllColorsIncluding": "Hiển thị tất cả màu (bao gồm không có sẵn)",
+ "chromaWeight": "Trọng số độ bão hòa",
+ "downloadPreview": "Tải xuống xem trước",
+ "apply": "Áp dụng",
+ "cancel": "Hủy",
+ "fit": "Vừa khung",
+ "hundred": "100%",
+ "clear": "Xóa",
+ "invert": "Đảo ngược",
+ "reprocessingOverlay": "Đang xử lý lại lớp phủ...",
+ "overlayUpdated": "Lớp phủ đã cập nhật!",
+ "notificationsEnabled": "Đã bật thông báo.",
+ "notificationsPermissionDenied": "Quyền thông báo bị từ chối.",
+ "overlayEnabled": "Đã bật lớp phủ.",
+ "overlayDisabled": "Đã tắt lớp phủ.",
+ "tokenSourceSet": "Nguồn token được đặt thành: {source}",
+ "batchModeSet": "Chế độ lô được đặt thành: {mode}",
+ "randomRange": "Phạm vi ngẫu nhiên",
+ "normalFixedSize": "Kích thước cố định bình thường",
+ "advancedColorSettingsReset": "Đã đặt lại cài đặt màu nâng cao.",
+ "shiftRowAltColumn": "Shift = Hàng • Alt = Cột",
+ "hideTurnstileBtn": "Ẩn",
+ "turnstileInstructions": "Cloudflare Turnstile — vui lòng hoàn thành kiểm tra nếu được hiển thị",
+ "uploadImageFirstColors": "Vui lòng tải lên hình ảnh trước để bắt màu có sẵn",
+ "availableColors": "Màu có sẵn ({count})",
+ "colorTooltip": "ID: {id}\nRGB: {rgb}",
+ "expandMode": "Chế độ mở rộng",
+ "minimize": "Thu nhỏ",
+ "restore": "Khôi phục",
+ "hideStats": "Ẩn thống kê",
+ "paintOptions": "Tùy chọn vẽ",
+ "paintWhitePixels": "Vẽ điểm ảnh trắng",
+ "paintTransparentPixels": "Vẽ điểm ảnh trong suốt"
+}
diff --git a/lang/zh-CN.json b/lang/zh-CN.json
new file mode 100644
index 00000000..9058c372
--- /dev/null
+++ b/lang/zh-CN.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace 自动图像",
+ "toggleOverlay": "切换覆盖层",
+ "scanColors": "扫描颜色",
+ "uploadImage": "上传图像",
+ "resizeImage": "调整大小",
+ "selectPosition": "选择位置",
+ "startPainting": "开始绘制",
+ "stopPainting": "停止绘制",
+ "checkingColors": "🔍 正在检查可用颜色...",
+ "noColorsFound": "❌ 请在网站上打开调色板后再试!",
+ "colorsFound": "✅ 找到 {count} 个可用颜色,准备上传。",
+ "loadingImage": "🖼️ 正在加载图像...",
+ "imageLoaded": "✅ 图像已加载,包含 {count} 个有效像素",
+ "imageError": "❌ 加载图像时出错",
+ "selectPositionAlert": "请在你想让作品开始的位置绘制第一个像素!",
+ "waitingPosition": "👆 正在等待你绘制参考像素...",
+ "positionSet": "✅ 位置设置成功!",
+ "positionTimeout": "❌ 选择位置超时",
+ "startPaintingMsg": "🎨 开始绘制...",
+ "paintingProgress": "🧱 进度: {painted}/{total} 像素...",
+ "noCharges": "⌛ 无可用次数,等待 {time}...",
+ "paintingStopped": "⏹️ 已被用户停止",
+ "paintingComplete": "✅ 绘制完成!共绘制 {count} 个像素。",
+ "paintingError": "❌ 绘制过程中出错",
+ "missingRequirements": "❌ 请先加载图像并选择位置",
+ "progress": "进度",
+ "pixels": "像素",
+ "charges": "次数",
+ "estimatedTime": "预计时间",
+ "initMessage": "点击\"上传图像\"开始",
+ "waitingInit": "正在等待初始化...",
+ "initializingToken": "🔧 正在初始化 Turnstile 令牌生成器...",
+ "tokenReady": "✅ 令牌生成器已就绪 - 可以开始绘制!",
+ "tokenRetryLater": "⚠️ 令牌生成器稍后将重试",
+ "resizeSuccess": "✅ 图像已调整为 {width}x{height}",
+ "paintingPaused": "⏸️ 在位置 X: {x}, Y: {y} 暂停",
+ "captchaNeeded": "❗ 令牌生成失败,请稍后再试。",
+ "saveData": "保存进度",
+ "loadData": "加载进度",
+ "saveToFile": "保存到文件",
+ "loadFromFile": "从文件加载",
+ "dataManager": "数据管理",
+ "autoSaved": "✅ 进度已自动保存",
+ "dataLoaded": "✅ 进度加载成功",
+ "fileSaved": "✅ 已成功保存到文件",
+ "fileLoaded": "✅ 已成功从文件加载",
+ "noSavedData": "❌ 未找到已保存进度",
+ "savedDataFound": "✅ 找到已保存进度!是否加载继续?",
+ "savedDate": "保存时间: {date}",
+ "clickLoadToContinue": "点击\"加载进度\"继续。",
+ "fileError": "❌ 处理文件时出错",
+ "invalidFileFormat": "❌ 文件格式无效",
+ "paintingSpeed": "绘制速度",
+ "pixelsPerSecond": "像素/秒",
+ "speedSetting": "速度: {speed} 像素/秒",
+ "settings": "设置",
+ "botSettings": "机器人设置",
+ "close": "关闭",
+ "language": "语言",
+ "themeSettings": "主题设置",
+ "themeSettingsDesc": "为界面选择你喜欢的配色主题。",
+ "languageSelectDesc": "选择你偏好的语言,变更立即生效。",
+ "autoCaptcha": "自动 CAPTCHA 解决",
+ "autoCaptchaDesc": "使用集成的生成器自动生成 Turnstile 令牌,必要时回退到浏览器自动化。",
+ "applySettings": "应用设置",
+ "settingsSaved": "✅ 设置保存成功!",
+ "speedOn": "开启",
+ "speedOff": "关闭",
+ "cooldownSettings": "冷却设置",
+ "waitCharges": "等待次数达到",
+ "captchaSolving": "🔑 正在生成 Turnstile 令牌...",
+ "captchaFailed": "❌ 令牌生成失败。尝试回退方法...",
+ "automation": "自动化",
+ "noChargesThreshold": "⌛ 等待次数达到 {threshold}。当前 {current}。下次在 {time}...",
+ "tokenCapturedSuccess": "令牌捕获成功!现在可以启动机器人。",
+ "notificationsNotSupported": "此浏览器不支持通知。",
+ "chargesReadyNotification": "WPlace — 次数就绪",
+ "chargesReadyMessage": "次数就绪:{current} / {max}。阈值:{threshold}。",
+ "testNotificationTitle": "WPlace — 测试",
+ "testNotificationMessage": "这是一个测试通知。",
+ "showStats": "显示统计",
+ "compactMode": "紧凑模式",
+ "refreshCharges": "刷新次数",
+ "closeStats": "关闭统计",
+ "zoomOut": "缩小",
+ "zoomIn": "放大",
+ "fitToView": "适合窗口",
+ "actualSize": "实际大小 (100%)",
+ "panMode": "平移(拖动移动视图)",
+ "clearIgnoredPixels": "清除所有忽略的像素",
+ "invertMask": "反转蒙版",
+ "waitingSetupComplete": "🔄 等待初始设置完成...",
+ "waitingTokenGenerator": "🔄 等待令牌生成器初始化...",
+ "uploadImageFirst": "请先上传图像以获取可用颜色",
+ "pleaseWaitInitialSetup": "🔄 请等待初始设置完成后再加载进度。",
+ "pleaseWaitFileSetup": "🔄 请等待初始设置完成后再从文件加载。",
+ "errorSavingProgress": "❌ 保存进度时出错",
+ "errorLoadingProgress": "❌ 加载进度时出错",
+ "fileOperationsAvailable": "📂 文件操作(加载/上传)现已可用!",
+ "tokenGeneratorReady": "🔑 令牌生成器准备就绪!",
+ "paintingStats": "绘制统计",
+ "enablePaintingSpeedLimit": "启用绘制速度限制(批次大小控制)",
+ "enableNotifications": "启用通知",
+ "notifyOnChargesThreshold": "次数达到阈值时通知",
+ "onlyWhenNotFocused": "仅在标签页未聚焦时",
+ "repeatEvery": "重复间隔",
+ "minutesPl": "分钟",
+ "grantPermission": "授予权限",
+ "test": "测试",
+ "showAllColorsIncluding": "显示所有颜色(包括不可用)",
+ "chromaWeight": "色度权重",
+ "downloadPreview": "下载预览",
+ "apply": "应用",
+ "cancel": "取消",
+ "fit": "适合",
+ "hundred": "100%",
+ "clear": "清除",
+ "invert": "反转",
+ "reprocessingOverlay": "重新处理覆盖层...",
+ "overlayUpdated": "覆盖层已更新!",
+ "notificationsEnabled": "已启用通知。",
+ "notificationsPermissionDenied": "通知权限被拒绝。",
+ "overlayEnabled": "已启用覆盖层。",
+ "overlayDisabled": "已禁用覆盖层。",
+ "tokenSourceSet": "令牌源设置为:{source}",
+ "batchModeSet": "批次模式设置为:{mode}",
+ "randomRange": "随机范围",
+ "normalFixedSize": "正常固定大小",
+ "advancedColorSettingsReset": "已重置高级颜色设置。",
+ "shiftRowAltColumn": "Shift = 行 • Alt = 列",
+ "hideTurnstileBtn": "隐藏",
+ "turnstileInstructions": "Cloudflare Turnstile — 如有显示请完成验证",
+ "uploadImageFirstColors": "请先上传图像以获取可用颜色",
+ "availableColors": "可用颜色 ({count})",
+ "colorTooltip": "ID:{id}\nRGB:{rgb}",
+ "expandMode": "展开模式",
+ "minimize": "最小化",
+ "restore": "恢复",
+ "hideStats": "隐藏统计",
+ "paintOptions": "绘图选项",
+ "paintWhitePixels": "绘制白色像素",
+ "paintTransparentPixels": "绘制透明像素"
+}
diff --git a/lang/zh-TW.json b/lang/zh-TW.json
new file mode 100644
index 00000000..384886ea
--- /dev/null
+++ b/lang/zh-TW.json
@@ -0,0 +1,144 @@
+{
+ "title": "WPlace 自動圖像",
+ "toggleOverlay": "切換覆蓋層",
+ "scanColors": "掃描顏色",
+ "uploadImage": "上傳圖像",
+ "resizeImage": "調整大小",
+ "selectPosition": "選擇位置",
+ "startPainting": "開始繪製",
+ "stopPainting": "停止繪製",
+ "checkingColors": "🔍 正在檢查可用顏色...",
+ "noColorsFound": "❌ 請在網站上打開調色板後再試!",
+ "colorsFound": "✅ 找到 {count} 個可用顏色,準備上傳。",
+ "loadingImage": "🖼️ 正在載入圖像...",
+ "imageLoaded": "✅ 圖像已載入,包含 {count} 個有效像素",
+ "imageError": "❌ 載入圖像時出錯",
+ "selectPositionAlert": "請在你想讓作品開始的位置繪製第一個像素!",
+ "waitingPosition": "👆 正在等待你繪製參考像素...",
+ "positionSet": "✅ 位置設定成功!",
+ "positionTimeout": "❌ 選擇位置逾時",
+ "startPaintingMsg": "🎨 開始繪製...",
+ "paintingProgress": "🧱 進度: {painted}/{total} 像素...",
+ "noCharges": "⌛ 無可用次數,等待 {time}...",
+ "paintingStopped": "⏹️ 已被使用者停止",
+ "paintingComplete": "✅ 繪製完成!共繪製 {count} 個像素。",
+ "paintingError": "❌ 繪製過程中出錯",
+ "missingRequirements": "❌ 請先載入圖像並選擇位置",
+ "progress": "進度",
+ "pixels": "像素",
+ "charges": "次數",
+ "estimatedTime": "預計時間",
+ "initMessage": "點擊「上傳圖像」開始",
+ "waitingInit": "正在等待初始化...",
+ "initializingToken": "🔧 正在初始化 Turnstile 令牌產生器...",
+ "tokenReady": "✅ 令牌產生器已就緒 - 可以開始繪製!",
+ "tokenRetryLater": "⚠️ 令牌產生器稍後將重試",
+ "resizeSuccess": "✅ 圖像已調整為 {width}x{height}",
+ "paintingPaused": "⏸️ 在位置 X: {x}, Y: {y} 暫停",
+ "captchaNeeded": "❗ 令牌產生失敗,請稍後再試。",
+ "saveData": "儲存進度",
+ "loadData": "載入進度",
+ "saveToFile": "儲存至檔案",
+ "loadFromFile": "從檔案載入",
+ "dataManager": "資料管理",
+ "autoSaved": "✅ 進度已自動儲存",
+ "dataLoaded": "✅ 進度載入成功",
+ "fileSaved": "✅ 已成功儲存至檔案",
+ "fileLoaded": "✅ 已成功從檔案載入",
+ "noSavedData": "❌ 未找到已儲存進度",
+ "savedDataFound": "✅ 找到已儲存進度!是否載入以繼續?",
+ "savedDate": "儲存時間: {date}",
+ "clickLoadToContinue": "點擊「載入進度」繼續。",
+ "fileError": "❌ 處理檔案時出錯",
+ "invalidFileFormat": "❌ 檔案格式無效",
+ "paintingSpeed": "繪製速度",
+ "pixelsPerSecond": "像素/秒",
+ "speedSetting": "速度: {speed} 像素/秒",
+ "settings": "設定",
+ "botSettings": "機器人設定",
+ "close": "關閉",
+ "language": "語言",
+ "themeSettings": "主題設定",
+ "themeSettingsDesc": "為介面選擇你喜歡的配色主題。",
+ "languageSelectDesc": "選擇你偏好的語言,變更立即生效。",
+ "autoCaptcha": "自動 CAPTCHA 解決",
+ "autoCaptchaDesc": "使用整合的產生器自動產生 Turnstile 令牌,必要時回退到瀏覽器自動化。",
+ "applySettings": "套用設定",
+ "settingsSaved": "✅ 設定儲存成功!",
+ "speedOn": "開啟",
+ "speedOff": "關閉",
+ "cooldownSettings": "冷卻設定",
+ "waitCharges": "等待次數達到",
+ "captchaSolving": "🔑 正在產生 Turnstile 令牌...",
+ "captchaFailed": "❌ 令牌產生失敗。嘗試回退方法...",
+ "automation": "自動化",
+ "noChargesThreshold": "⌛ 等待次數達到 {threshold}。目前 {current}。下次在 {time}...",
+ "tokenCapturedSuccess": "令牌捕獲成功!現在可以啟動機器人。",
+ "notificationsNotSupported": "此瀏覽器不支持通知。",
+ "chargesReadyNotification": "WPlace — 次數就緒",
+ "chargesReadyMessage": "次數就緒:{current} / {max}。閾值:{threshold}。",
+ "testNotificationTitle": "WPlace — 測試",
+ "testNotificationMessage": "這是一個測試通知。",
+ "showStats": "顯示統計",
+ "compactMode": "緊凑模式",
+ "refreshCharges": "刷新次數",
+ "closeStats": "關閉統計",
+ "zoomOut": "縮小",
+ "zoomIn": "放大",
+ "fitToView": "適合視窗",
+ "actualSize": "實際大小 (100%)",
+ "panMode": "平移(拖拉移動視圖)",
+ "clearIgnoredPixels": "清除所有忽略的像素",
+ "invertMask": "反轉遮罩",
+ "waitingSetupComplete": "🔄 等待初始設定完成...",
+ "waitingTokenGenerator": "🔄 等待令牌產生器初始化...",
+ "uploadImageFirst": "請先上傳圖像以獲取可用顏色",
+ "pleaseWaitInitialSetup": "🔄 請等待初始設定完成後再載入進度。",
+ "pleaseWaitFileSetup": "🔄 請等待初始設定完成後再從檔案載入。",
+ "errorSavingProgress": "❌ 儲存進度時出錯",
+ "errorLoadingProgress": "❌ 載入進度時出錯",
+ "fileOperationsAvailable": "📂 檔案操作(載入/上傳)現已可用!",
+ "tokenGeneratorReady": "🔑 令牌產生器準備就緒!",
+ "paintingStats": "繪制統計",
+ "enablePaintingSpeedLimit": "啟用繪制速度限制(批次大小控制)",
+ "enableNotifications": "啟用通知",
+ "notifyOnChargesThreshold": "次數達到闾值時通知",
+ "onlyWhenNotFocused": "僅在標籤頁未聚焦時",
+ "repeatEvery": "重複間隔",
+ "minutesPl": "分鐘",
+ "grantPermission": "授予權限",
+ "test": "測試",
+ "showAllColorsIncluding": "顯示所有顏色(包括不可用)",
+ "chromaWeight": "色度權重",
+ "downloadPreview": "下載預覽",
+ "apply": "套用",
+ "cancel": "取消",
+ "fit": "適合",
+ "hundred": "100%",
+ "clear": "清除",
+ "invert": "反轉",
+ "reprocessingOverlay": "重新處理覆蓋層...",
+ "overlayUpdated": "覆蓋層已更新!",
+ "notificationsEnabled": "已啟用通知。",
+ "notificationsPermissionDenied": "通知權限被拒絕。",
+ "overlayEnabled": "已啟用覆蓋層。",
+ "overlayDisabled": "已禁用覆蓋層。",
+ "tokenSourceSet": "令牌來源設定為:{source}",
+ "batchModeSet": "批次模式設定為:{mode}",
+ "randomRange": "隨機範圍",
+ "normalFixedSize": "正常固定大小",
+ "advancedColorSettingsReset": "已重置進階顏色設定。",
+ "shiftRowAltColumn": "Shift = 列 • Alt = 行",
+ "hideTurnstileBtn": "隱藏",
+ "turnstileInstructions": "Cloudflare Turnstile — 如有顯示請完成驗證",
+ "uploadImageFirstColors": "請先上傳圖像以獲取可用顏色",
+ "availableColors": "可用顏色 ({count})",
+ "colorTooltip": "ID:{id}\nRGB:{rgb}",
+ "expandMode": "展開模式",
+ "minimize": "最小化",
+ "restore": "恢復",
+ "hideStats": "隱藏統計",
+ "paintOptions": "繪圖選項",
+ "paintWhitePixels": "繪製白色像素",
+ "paintTransparentPixels": "繪製透明像素"
+}
diff --git a/scripts/RepairTool.js b/scripts/RepairTool.js
new file mode 100644
index 00000000..c660d14f
--- /dev/null
+++ b/scripts/RepairTool.js
@@ -0,0 +1,2593 @@
+// eslint-disable-next-line prettier/prettier
+; (async () => {
+ // CONFIGURATION CONSTANTS
+ const CONFIG = {
+ COOLDOWN_DEFAULT: 31000,
+ TRANSPARENCY_THRESHOLD: 100,
+ WHITE_THRESHOLD: 250,
+ LOG_INTERVAL: 10,
+ PAINTING_SPEED: {
+ MIN: 1,
+ MAX: 1000,
+ DEFAULT: 5,
+ },
+ TOKEN_SOURCE: 'generator', // "generator", "manual", or "hybrid"
+ AUTONOMOUS_MODE: true, // Enable autonomous operation
+ AUTO_TOKEN_REFRESH: true, // Automatically refresh tokens
+ TOKEN_PRELOAD_BUFFER: 60000, // Preload tokens 1 minute before expiry
+ MAX_RETRIES: 10,
+ RETRY_DELAY_BASE: 1000,
+ COLOR_MAP: {
+ 0: { id: 1, name: 'Black', rgb: { r: 0, g: 0, b: 0 } },
+ 1: { id: 2, name: 'Dark Gray', rgb: { r: 60, g: 60, b: 60 } },
+ 2: { id: 3, name: 'Gray', rgb: { r: 120, g: 120, b: 120 } },
+ 3: { id: 4, name: 'Light Gray', rgb: { r: 210, g: 210, b: 210 } },
+ 4: { id: 5, name: 'White', rgb: { r: 255, g: 255, b: 255 } },
+ 5: { id: 6, name: 'Deep Red', rgb: { r: 96, g: 0, b: 24 } },
+ 6: { id: 7, name: 'Red', rgb: { r: 237, g: 28, b: 36 } },
+ 7: { id: 8, name: 'Orange', rgb: { r: 255, g: 127, b: 39 } },
+ 8: { id: 9, name: 'Gold', rgb: { r: 246, g: 170, b: 9 } },
+ 9: { id: 10, name: 'Yellow', rgb: { r: 249, g: 221, b: 59 } },
+ 10: { id: 11, name: 'Light Yellow', rgb: { r: 255, g: 250, b: 188 } },
+ 11: { id: 12, name: 'Dark Green', rgb: { r: 14, g: 185, b: 104 } },
+ 12: { id: 13, name: 'Green', rgb: { r: 19, g: 230, b: 123 } },
+ 13: { id: 14, name: 'Light Green', rgb: { r: 135, g: 255, b: 94 } },
+ 14: { id: 15, name: 'Dark Teal', rgb: { r: 12, g: 129, b: 110 } },
+ 15: { id: 16, name: 'Teal', rgb: { r: 16, g: 174, b: 166 } },
+ 16: { id: 17, name: 'Light Teal', rgb: { r: 19, g: 225, b: 190 } },
+ 17: { id: 20, name: 'Cyan', rgb: { r: 96, g: 247, b: 242 } },
+ 18: { id: 44, name: 'Light Cyan', rgb: { r: 187, g: 250, b: 242 } },
+ 19: { id: 18, name: 'Dark Blue', rgb: { r: 40, g: 80, b: 158 } },
+ 20: { id: 19, name: 'Blue', rgb: { r: 64, g: 147, b: 228 } },
+ 21: { id: 21, name: 'Indigo', rgb: { r: 107, g: 80, b: 246 } },
+ 22: { id: 22, name: 'Light Indigo', rgb: { r: 153, g: 177, b: 251 } },
+ 23: { id: 23, name: 'Dark Purple', rgb: { r: 120, g: 12, b: 153 } },
+ 24: { id: 24, name: 'Purple', rgb: { r: 170, g: 56, b: 185 } },
+ 25: { id: 25, name: 'Light Purple', rgb: { r: 224, g: 159, b: 249 } },
+ 26: { id: 26, name: 'Dark Pink', rgb: { r: 203, g: 0, b: 122 } },
+ 27: { id: 27, name: 'Pink', rgb: { r: 236, g: 31, b: 128 } },
+ 28: { id: 28, name: 'Light Pink', rgb: { r: 243, g: 141, b: 169 } },
+ 29: { id: 29, name: 'Dark Brown', rgb: { r: 104, g: 70, b: 52 } },
+ 30: { id: 30, name: 'Brown', rgb: { r: 149, g: 104, b: 42 } },
+ 31: { id: 31, name: 'Beige', rgb: { r: 248, g: 178, b: 119 } },
+ 32: { id: 52, name: 'Light Beige', rgb: { r: 255, g: 197, b: 165 } },
+ 33: { id: 32, name: 'Medium Gray', rgb: { r: 170, g: 170, b: 170 } },
+ 34: { id: 33, name: 'Dark Red', rgb: { r: 165, g: 14, b: 30 } },
+ 35: { id: 34, name: 'Light Red', rgb: { r: 250, g: 128, b: 114 } },
+ 36: { id: 35, name: 'Dark Orange', rgb: { r: 228, g: 92, b: 26 } },
+ 37: { id: 37, name: 'Dark Goldenrod', rgb: { r: 156, g: 132, b: 49 } },
+ 38: { id: 38, name: 'Goldenrod', rgb: { r: 197, g: 173, b: 49 } },
+ 39: { id: 39, name: 'Light Goldenrod', rgb: { r: 232, g: 212, b: 95 } },
+ 40: { id: 40, name: 'Dark Olive', rgb: { r: 74, g: 107, b: 58 } },
+ 41: { id: 41, name: 'Olive', rgb: { r: 90, g: 148, b: 74 } },
+ 42: { id: 42, name: 'Light Olive', rgb: { r: 132, g: 197, b: 115 } },
+ 43: { id: 43, name: 'Dark Cyan', rgb: { r: 15, g: 121, b: 159 } },
+ 44: { id: 45, name: 'Light Blue', rgb: { r: 125, g: 199, b: 255 } },
+ 45: { id: 46, name: 'Dark Indigo', rgb: { r: 77, g: 49, b: 184 } },
+ 46: { id: 47, name: 'Dark Slate Blue', rgb: { r: 74, g: 66, b: 132 } },
+ 47: { id: 48, name: 'Slate Blue', rgb: { r: 122, g: 113, b: 196 } },
+ 48: { id: 49, name: 'Light Slate Blue', rgb: { r: 181, g: 174, b: 241 } },
+ 49: { id: 53, name: 'Dark Peach', rgb: { r: 155, g: 82, b: 73 } },
+ 50: { id: 54, name: 'Peach', rgb: { r: 209, g: 128, b: 120 } },
+ 51: { id: 55, name: 'Light Peach', rgb: { r: 250, g: 182, b: 164 } },
+ 52: { id: 50, name: 'Light Brown', rgb: { r: 219, g: 164, b: 99 } },
+ 53: { id: 56, name: 'Dark Tan', rgb: { r: 123, g: 99, b: 82 } },
+ 54: { id: 57, name: 'Tan', rgb: { r: 156, g: 132, b: 107 } },
+ 55: { id: 36, name: 'Light Tan', rgb: { r: 214, g: 181, b: 148 } },
+ 56: { id: 51, name: 'Dark Beige', rgb: { r: 209, g: 128, b: 81 } },
+ 57: { id: 61, name: 'Dark Stone', rgb: { r: 109, g: 100, b: 63 } },
+ 58: { id: 62, name: 'Stone', rgb: { r: 148, g: 140, b: 107 } },
+ 59: { id: 63, name: 'Light Stone', rgb: { r: 205, g: 197, b: 158 } },
+ 60: { id: 58, name: 'Dark Slate', rgb: { r: 51, g: 57, b: 65 } },
+ 61: { id: 59, name: 'Slate', rgb: { r: 109, g: 117, b: 141 } },
+ 62: { id: 60, name: 'Light Slate', rgb: { r: 179, g: 185, b: 209 } },
+ 63: { id: 0, name: 'Transparent', rgb: null },
+ },
+ };
+
+ // GLOBAL STATE
+ const state = {
+ running: false,
+ imageLoaded: false,
+ totalPixels: 0,
+ paintedPixels: 0,
+ availableColors: [],
+ displayCharges: 0,
+ maxCharges: 1,
+ cooldown: CONFIG.COOLDOWN_DEFAULT,
+ imageData: null,
+ stopFlag: false,
+ startPosition: null,
+ region: null,
+ paintWhitePixels: true,
+ paintTransparentPixels: false,
+ autoRepairEnabled: false,
+ autoRepairInterval: 30,
+ autoRepairTimer: null,
+ debugLogs: [],
+ customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD,
+ customWhiteThreshold: CONFIG.WHITE_THRESHOLD,
+ tokenSource: CONFIG.TOKEN_SOURCE,
+ autonomousMode: CONFIG.AUTONOMOUS_MODE,
+ autoTokenRefresh: CONFIG.AUTO_TOKEN_REFRESH,
+ tokenPreloadBuffer: CONFIG.TOKEN_PRELOAD_BUFFER,
+ retryCount: 0,
+ tokenRetryTimer: null,
+ tokenPreloadTimer: null,
+ windowMinimized: false,
+ lastAttackState: null,
+ initialSetupComplete: false,
+ };
+
+ // Random string generator
+ const randStr = (len, chars = 'abcdefghijklmnopqrstuvwxyz0123456789') =>
+ [...Array(len)].map(() => chars[(crypto?.getRandomValues?.(new Uint32Array(1))[0] % chars.length) || Math.floor(Math.random() * chars.length)]).join('');
+
+ const fpStr32 = randStr(32);
+
+ // Enhanced Turnstile token handling - Actualizado con la lógica de new.txt
+ let turnstileToken = null;
+ let tokenExpiryTime = 0;
+ let tokenGenerationInProgress = false;
+ let _resolveToken = null;
+ let tokenPromise = new Promise((resolve) => {
+ _resolveToken = resolve;
+ });
+ let retryCount = 0;
+ const MAX_RETRIES = 10;
+ const MAX_BATCH_RETRIES = 10;
+ const TOKEN_LIFETIME = 240000; // 4 minutes (tokens typically last 5 min, use 4 for safety)
+
+ function setTurnstileToken(token) {
+ if (_resolveToken) {
+ _resolveToken(token);
+ _resolveToken = null;
+ }
+ turnstileToken = token;
+ tokenExpiryTime = Date.now() + TOKEN_LIFETIME;
+ console.log('✅ Turnstile token set successfully');
+ Utils.addDebugLog('Token cached successfully', 'success');
+ }
+
+ function isTokenValid() {
+ return turnstileToken && Date.now() < tokenExpiryTime;
+ }
+
+ function invalidateToken() {
+ turnstileToken = null;
+ tokenExpiryTime = 0;
+ console.log('🗑️ Token invalidated, will force fresh generation');
+ Utils.addDebugLog('Token invalidated, will force fresh generation', 'warning');
+ }
+
+ async function ensureToken(forceRefresh = false) {
+ // Return cached token if still valid and not forcing refresh
+ if (isTokenValid() && !forceRefresh) {
+ return turnstileToken;
+ }
+
+ // Invalidate token if forcing refresh
+ if (forceRefresh) invalidateToken();
+
+ // Avoid multiple simultaneous token generations
+ if (tokenGenerationInProgress) {
+ console.log('🔄 Token generation already in progress, waiting...');
+ await Utils.sleep(2000);
+ return isTokenValid() ? turnstileToken : null;
+ }
+
+ tokenGenerationInProgress = true;
+
+ try {
+ console.log('🔄 Token expired or missing, generating new one...');
+ const token = await handleCaptchaWithRetry();
+ if (token && token.length > 20) {
+ setTurnstileToken(token);
+ console.log('✅ Token captured and cached successfully');
+ return token;
+ }
+
+ console.log('⚠️ Invisible Turnstile failed, forcing browser automation...');
+ const fallbackToken = await handleCaptchaFallback();
+ if (fallbackToken && fallbackToken.length > 20) {
+ setTurnstileToken(fallbackToken);
+ console.log('✅ Fallback token captured successfully');
+ return fallbackToken;
+ }
+
+ console.log('❌ All token generation methods failed');
+ return null;
+ } finally {
+ tokenGenerationInProgress = false;
+ }
+ }
+
+ async function handleCaptchaWithRetry() {
+ const startTime = performance.now();
+
+ try {
+ const { sitekey, token: preGeneratedToken } = await Utils.obtainSitekeyAndToken();
+
+ if (!sitekey) {
+ throw new Error('No valid sitekey found');
+ }
+
+ console.log('🔑 Using sitekey:', sitekey);
+
+ if (typeof window !== 'undefined' && window.navigator) {
+ console.log(
+ '🧭 UA:',
+ window.navigator.userAgent.substring(0, 50) + '...',
+ 'Platform:',
+ window.navigator.platform
+ );
+ }
+
+ let token = null;
+
+ if (
+ preGeneratedToken &&
+ typeof preGeneratedToken === 'string' &&
+ preGeneratedToken.length > 20
+ ) {
+ console.log('♻️ Reusing pre-generated Turnstile token');
+ token = preGeneratedToken;
+ } else {
+ if (isTokenValid()) {
+ console.log('♻️ Using existing cached token (from previous session)');
+ token = turnstileToken;
+ } else {
+ console.log('🔍 Generating new token with executeTurnstile...');
+ token = await Utils.executeTurnstile(sitekey, 'paint');
+ if (token) setTurnstileToken(token);
+ }
+ }
+
+ if (token && typeof token === 'string' && token.length > 20) {
+ const elapsed = Math.round(performance.now() - startTime);
+ console.log(`✅ Turnstile token generated successfully in ${elapsed}ms`);
+ return token;
+ } else {
+ throw new Error(`Invalid or empty token received - Length: ${token?.length || 0}`);
+ }
+ } catch (error) {
+ const elapsed = Math.round(performance.now() - startTime);
+ console.error(`❌ Turnstile token generation failed after ${elapsed}ms:`, error);
+ throw error;
+ }
+ }
+
+ async function handleCaptchaFallback() {
+ return new Promise(async (resolve, reject) => {
+ try {
+ // Ensure we have a fresh promise to await for a new token capture
+ if (!_resolveToken) {
+ tokenPromise = new Promise((res) => {
+ _resolveToken = res;
+ });
+ }
+ const timeoutPromise = Utils.sleep(20000).then(() =>
+ reject(new Error('Auto-CAPTCHA timed out.'))
+ );
+
+ const solvePromise = (async () => {
+ const mainPaintBtn = await Utils.waitForSelector(
+ 'button.btn.btn-primary.btn-lg, button.btn-primary.sm\\:btn-xl',
+ 200,
+ 10000
+ );
+ if (!mainPaintBtn) throw new Error('Could not find the main paint button.');
+ mainPaintBtn.click();
+ await Utils.sleep(500);
+
+ const transBtn = await Utils.waitForSelector('button#color-0', 200, 5000);
+ if (!transBtn) throw new Error('Could not find the transparent color button.');
+ transBtn.click();
+ await Utils.sleep(500);
+
+ const canvas = await Utils.waitForSelector('canvas', 200, 5000);
+ if (!canvas) throw new Error('Could not find the canvas element.');
+
+ canvas.setAttribute('tabindex', '0');
+ canvas.focus();
+ const rect = canvas.getBoundingClientRect();
+ const centerX = Math.round(rect.left + rect.width / 2);
+ const centerY = Math.round(rect.top + rect.height / 2);
+
+ canvas.dispatchEvent(
+ new MouseEvent('mousemove', {
+ clientX: centerX,
+ clientY: centerY,
+ bubbles: true,
+ })
+ );
+ canvas.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ key: ' ',
+ code: 'Space',
+ bubbles: true,
+ })
+ );
+ await Utils.sleep(50);
+ canvas.dispatchEvent(
+ new KeyboardEvent('keyup', {
+ key: ' ',
+ code: 'Space',
+ bubbles: true,
+ })
+ );
+ await Utils.sleep(500);
+
+ // 800ms delay before sending confirmation
+ await Utils.sleep(800);
+
+ // Keep confirming until token is captured
+ const confirmLoop = async () => {
+ while (!turnstileToken) {
+ let confirmBtn = await Utils.waitForSelector(
+ 'button.btn.btn-primary.btn-lg, button.btn.btn-primary.sm\\:btn-xl'
+ );
+ if (!confirmBtn) {
+ const allPrimary = Array.from(document.querySelectorAll('button.btn-primary'));
+ confirmBtn = allPrimary.length ? allPrimary[allPrimary.length - 1] : null;
+ }
+ if (confirmBtn) {
+ confirmBtn.click();
+ }
+ await Utils.sleep(500); // 500ms delay between confirmation attempts
+ }
+ };
+
+ // Start confirmation loop and wait for token
+ confirmLoop();
+ const token = await tokenPromise;
+ await Utils.sleep(300); // small delay after token is captured
+ resolve(token);
+ })();
+
+ await Promise.race([solvePromise, timeoutPromise]);
+ } catch (error) {
+ console.error('Auto-CAPTCHA process failed:', error);
+ reject(error);
+ }
+ });
+ }
+
+ // NUEVA LÓGICA DE INYECCIÓN - ACTUALIZADA DESDE new.txt
+ function inject(callback) {
+ const script = document.createElement('script');
+ script.textContent = `(${callback})();`;
+ document.documentElement?.appendChild(script);
+ script.remove();
+ }
+
+ inject(() => {
+ const fetchedBlobQueue = new Map();
+
+ window.addEventListener('message', (event) => {
+ const { source, blobID, blobData } = event.data;
+ if (source === 'auto-image-overlay' && blobID && blobData) {
+ const callback = fetchedBlobQueue.get(blobID);
+ if (typeof callback === 'function') {
+ callback(blobData);
+ }
+ fetchedBlobQueue.delete(blobID);
+ }
+ });
+
+ const originalFetch = window.fetch;
+ window.fetch = async function (...args) {
+ const response = await originalFetch.apply(this, args);
+ const url = args[0] instanceof Request ? args[0].url : args[0];
+
+ if (typeof url === 'string') {
+ if (url.includes('https://backend.wplace.live/s0/pixel/')) {
+ try {
+ const payload = JSON.parse(args[1].body);
+ if (payload.t) {
+ // 📊 Debug log
+ console.log(
+ `🔍✅ Turnstile Token Captured - Type: ${typeof payload.t}, Value: ${payload.t
+ ? typeof payload.t === 'string'
+ ? payload.t.length > 50
+ ? payload.t.substring(0, 50) + '...'
+ : payload.t
+ : JSON.stringify(payload.t)
+ : 'null/undefined'
+ }, Length: ${payload.t?.length || 0}`
+ );
+ window.postMessage({ source: 'turnstile-capture', token: payload.t }, '*');
+ }
+ } catch (_) {
+ /* ignore */
+ }
+ }
+
+ const contentType = response.headers.get('content-type') || '';
+ if (contentType.includes('image/png') && url.includes('.png')) {
+ const cloned = response.clone();
+ return new Promise(async (resolve) => {
+ const blobUUID = crypto.randomUUID();
+ const originalBlob = await cloned.blob();
+
+ fetchedBlobQueue.set(blobUUID, (processedBlob) => {
+ resolve(
+ new Response(processedBlob, {
+ headers: cloned.headers,
+ status: cloned.status,
+ statusText: cloned.statusText,
+ })
+ );
+ });
+
+ window.postMessage(
+ {
+ source: 'auto-image-tile',
+ endpoint: url,
+ blobID: blobUUID,
+ blobData: originalBlob,
+ },
+ '*'
+ );
+ });
+ }
+ }
+
+ return response;
+ };
+ });
+
+ window.addEventListener('message', (event) => {
+ const { source, endpoint, blobID, blobData, token } = event.data;
+
+ if (source === 'auto-image-tile' && endpoint && blobID && blobData) {
+ overlayManager.processAndRespondToTileRequest(event.data);
+ }
+
+ if (source === 'turnstile-capture' && token) {
+ setTurnstileToken(token);
+ Utils.addDebugLog('Token captured from injection system', 'success');
+ updateTokenStatus();
+ }
+ });
+
+ // NUEVO SISTEMA WASM - ACTUALIZADO DESDE new.txt
+ var pawtect_chunk = null;
+
+ // Find module if pawtect_chunk is null
+ pawtect_chunk ??= await findTokenModule("pawtect_wasm_bg.wasm");
+
+ async function createWasmToken(regionX, regionY, payload) {
+ try {
+ // Load the Pawtect module and WASM
+ const mod = await import(new URL('/_app/immutable/chunks/'+pawtect_chunk, location.origin).href);
+ let wasm;
+ try {
+ wasm = await mod._();
+ console.log('✅ WASM initialized successfully');
+ } catch (wasmError) {
+ console.error('❌ WASM initialization failed:', wasmError);
+ return null;
+ }
+ try {
+ try {
+ const me = await fetch(`https://backend.wplace.live/me`, { credentials: 'include' }).then(r => r.ok ? r.json() : null);
+ if (me?.id) {
+ mod.i(me.id);
+ console.log('✅ user ID set:', me.id);
+ }
+ } catch { }
+ } catch (userIdError) {
+ console.log('⚠️ Error setting user ID:', userIdError.message);
+ }
+ try {
+ const testUrl = `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`;
+ if (mod.r) {
+ mod.r(testUrl);
+ console.log('✅ Request URL set:', testUrl);
+ } else {
+ console.log('⚠️ request_url function (mod.r) not available');
+ }
+ } catch (urlError) {
+ console.log('⚠️ Error setting request URL:', urlError.message);
+ }
+
+ console.log('🔍 payload:', payload);
+
+ // Encode payload
+ const enc = new TextEncoder();
+ const dec = new TextDecoder();
+ const bodyStr = JSON.stringify(payload);
+ const bytes = enc.encode(bodyStr);
+ console.log('🔍 Payload size:', bytes.length, 'bytes');
+ console.log('🔄 Payload string:', bodyStr);
+
+ // Allocate WASM memory with validation
+ let inPtr;
+ try {
+ if (!wasm.__wbindgen_malloc) {
+ console.error('❌ __wbindgen_malloc function not found');
+ return null;
+ }
+
+ inPtr = wasm.__wbindgen_malloc(bytes.length, 1);
+ console.log('✅ WASM memory allocated, pointer:', inPtr);
+
+ // Copy data to WASM memory
+ const wasmBuffer = new Uint8Array(wasm.memory.buffer, inPtr, bytes.length);
+ wasmBuffer.set(bytes);
+ console.log('✅ Data copied to WASM memory');
+ } catch (memError) {
+ console.error('❌ Memory allocation error:', memError);
+ return null;
+ }
+
+ // Call the WASM function
+ console.log('🚀 Calling get_pawtected_endpoint_payload...');
+ let outPtr, outLen, token;
+ try {
+ const result = wasm.get_pawtected_endpoint_payload(inPtr, bytes.length);
+ console.log('✅ Function called, result type:', typeof result, result);
+
+ if (Array.isArray(result) && result.length === 2) {
+ [outPtr, outLen] = result;
+ console.log('✅ Got output pointer:', outPtr, 'length:', outLen);
+
+ // Decode the result
+ const outputBuffer = new Uint8Array(wasm.memory.buffer, outPtr, outLen);
+ token = dec.decode(outputBuffer);
+ console.log('✅ Token decoded successfully');
+ } else {
+ console.error('❌ Unexpected function result format:', result);
+ return null;
+ }
+ } catch (funcError) {
+ console.error('❌ Function call error:', funcError);
+ console.error('Stack trace:', funcError.stack);
+ return null;
+ }
+
+ // Cleanup memory
+ try {
+ if (wasm.__wbindgen_free && outPtr && outLen) {
+ wasm.__wbindgen_free(outPtr, outLen, 1);
+ console.log('✅ Output memory freed');
+ }
+ if (wasm.__wbindgen_free && inPtr) {
+ wasm.__wbindgen_free(inPtr, bytes.length, 1);
+ console.log('✅ Input memory freed');
+ }
+ } catch (cleanupError) {
+ console.log('⚠️ Cleanup warning:', cleanupError.message);
+ }
+
+ console.log('🎉 SUCCESS!');
+ console.log('🔑 Full token:', token);
+ return token;
+ } catch (error) {
+ console.error('❌ Failed to generate fp parameter:', error);
+ return null;
+ }
+ }
+
+ async function findTokenModule(str) {
+ console.log('🔎 Searching for wasm Module...');
+ const links = Array.from(document.querySelectorAll('link[rel="modulepreload"][href$=".js"]'));
+
+ for (const link of links) {
+ try {
+ const url = new URL(link.getAttribute("href"), location.origin).href;
+ const code = await fetch(url).then(r => r.text());
+ if (code.includes(str)) {
+ console.log('✅ Found wasm Module...');
+ return url.split('/').pop();
+ }
+ } catch { }
+ }
+ console.error(`❌ Could not find Pawtect chunk: `, error);
+ }
+
+ // Audio notification system
+ function playNotificationSound() {
+ try {
+ const audio = new Audio('https://cdn.pixabay.com/download/audio/2025/03/21/audio_9bec51b17f.mp3?filename=glass-break-316720.mp3');
+ audio.volume = 0.5;
+ audio.play().catch(() => {
+ console.warn('Could not play notification sound');
+ });
+ } catch (error) {
+ console.warn('Audio notification failed:', error);
+ }
+ }
+
+ // Notification system for attacks and repairs
+ function showAttackNotification(type, count = 0) {
+ const existing = document.getElementById('attack-notification');
+ if (existing) existing.remove();
+
+ const notification = document.createElement('div');
+ notification.id = 'attack-notification';
+ notification.style.cssText = `
+ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
+ z-index: 15000; background: rgba(0,0,0,0.9); color: white;
+ border-radius: 15px; padding: 20px; text-align: center;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.8);
+ border: 2px solid ${type === 'attack' ? '#ff4444' : '#44ff44'};
+ min-width: 350px; max-width: 450px;
+ `;
+
+ if (type === 'attack') {
+ notification.innerHTML = `
+