From 91878fb0e090e38a8518c03a562fd6a4e0d3c414 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:18:42 +0800 Subject: [PATCH 01/18] Delete .github directory --- .github/ISSUE_TEMPLATE/bug-report.yml | 100 ---------- .github/ISSUE_TEMPLATE/config.yml | 5 - .github/ISSUE_TEMPLATE/feature-request.yml | 48 ----- .../pull_request_template.md | 28 --- .github/workflows/cd.yml | 187 ------------------ 5 files changed, 368 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml delete mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md delete mode 100644 .github/workflows/cd.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100644 index 48f7937..0000000 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,100 +0,0 @@ -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 deleted file mode 100644 index c92e633..0000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 8d442eb..0000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index 5b2ef76..0000000 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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 deleted file mode 100644 index 8d1c7f4..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,187 +0,0 @@ -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 "

${title}

" - if [[ "$DIR" != "$ROOT" ]]; then - echo "

⬆ Up • All branches

" - else - echo "

All branches

" - fi - echo "" - - # Subdirectories - while IFS= read -r d; do - [[ -n "$d" ]] || continue - printf "\n" "$d" "$d" - done < <(find "$DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort) - - # Files - while IFS= read -r f; do - [[ -n "$f" ]] || continue - size=$(stat -c %s "$DIR/$f" 2>/dev/null || echo 0) - size_h=$(numfmt --to=iec --suffix=B "$size" 2>/dev/null || echo "$size") - mod=$(date -r "$DIR/$f" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "") - printf "\n" "$f" "$f" "$size_h" "$mod" - done < <(find "$DIR" -mindepth 1 -maxdepth 1 -type f ! -name 'index.html' -printf '%f\n' | sort) - - echo "
NameSizeModified
📁 %s/——
📄 %s%s%s
" - echo "

Generated $(date -u '+%Y-%m-%d %H:%M UTC')

" - } > "$DIR/index.html" - } - - # Build per-branch trees and indexes - for BR in "${BRANCHES[@]}"; do - SAFE="$(echo "$BR" | tr '/:@ ' '----' | sed 's/[^A-Za-z0-9._-]/-/g')" - mkdir -p "public/$SAFE" - - git fetch --depth=1 origin "$BR" - git archive --format=tar FETCH_HEAD | tar -x -C "public/$SAFE" - - # Keep only static assets; prune empty dirs - rm -rf "public/$SAFE/.git" "public/$SAFE/.github" \ - "public/$SAFE/node_modules" - find "public/$SAFE" -type f ! \ - \( -name '*.html' -o -name '*.js' -o -name '*.css' -o \ - -name '*.json' -o -name '*.png' -o -name '*.jpg' -o \ - -name '*.jpeg' -o -name '*.gif' -o -name '*.svg' \) \ - -delete - find "public/$SAFE" -type d -empty -delete - - ROOT_DIR="public/$SAFE" - while IFS= read -r -d '' d; do - gen_index_dir "$d" "$ROOT_DIR" "$BR" - done < <(find "$ROOT_DIR" -type d -print0) - - # Append to landing page (JSON-escaped) - SAFE_JSON=$(printf '%s' "$SAFE" | jq -Rr @json) - BR_JSON=$(printf '%s' "$BR" | jq -Rr @json) - echo "" \ - >> public/index.html - done - - # Finish landing page - cat >> public/index.html <<'HTML' - - HTML - - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v4 - with: - path: public - - - name: Deploy to GitHub Pages - id: deploy - uses: actions/deploy-pages@v4 From c5460faa3ce33a1db77bb41273a72a8e0b482840 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:18:52 +0800 Subject: [PATCH 02/18] Delete AccountSwapper directory --- AccountSwapper/background.js | 85 ------------------------------------ AccountSwapper/content.js | 32 -------------- AccountSwapper/manifest.json | 27 ------------ 3 files changed, 144 deletions(-) delete mode 100644 AccountSwapper/background.js delete mode 100644 AccountSwapper/content.js delete mode 100644 AccountSwapper/manifest.json diff --git a/AccountSwapper/background.js b/AccountSwapper/background.js deleted file mode 100644 index 500060e..0000000 --- a/AccountSwapper/background.js +++ /dev/null @@ -1,85 +0,0 @@ -const cookieDomain = '.backend.wplace.live'; - -async function preserveAndResetJ() { - let savedValue = null; - - try { - const oldJ = await chrome.cookies.get({ - url: 'https://backend.wplace.live/', - name: 'j', - }); - - if (oldJ && oldJ.value) { - savedValue = oldJ.value; - console.log('[bg] Saved j cookie:', savedValue); - } else { - console.warn("[bg] No 'j' cookie found → will only nuke cf_clearance"); - } - - setTimeout(async () => { - await new Promise((resolve) => { - chrome.browsingData.remove({ origins: ['https://wplace.live'] }, { cookies: true }, () => { - console.log('[bg] Nuke done (delayed)'); - resolve(); - }); - }); - - if (savedValue) { - setCookie(savedValue); - } else { - console.log('[bg] No j to restore, leaving nuked'); - } - }, 2000); - } catch (err) { - console.error('[bg] Error in preserveAndResetJ:', err); - } -} - -chrome.webNavigation.onCompleted.addListener( - (details) => { - if (details.url.includes('wplace.live')) { - console.log('[bg] Page load detected → nuking cookies with double pass'); - preserveAndResetJ(); - } - }, - { url: [{ hostContains: 'wplace.live' }] } -); - -function setCookie(value) { - const cleaned = value.trim(); - console.log('[bg] setCookie CALLED with:', cleaned); - - chrome.cookies.set( - { - url: 'https://backend.wplace.live/', - name: 'j', - value: cleaned, - domain: cookieDomain, - path: '/', - }, - (cookie) => { - if (chrome.runtime.lastError) { - console.error('[bg] cookie set error:', chrome.runtime.lastError.message); - } else { - console.log('[bg] cookie set result:', cookie); - - chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { - if (tabs.length > 0) { - chrome.tabs.sendMessage(tabs[0].id, { - type: 'cookieSet', - value: cleaned, - }); - } - }); - } - } - ); -} - -chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => { - if (msg.type === 'setCookie' && msg.value) { - await setCookie(msg.value); - sendResponse({ status: 'ok' }); - } - return true; -}); diff --git a/AccountSwapper/content.js b/AccountSwapper/content.js deleted file mode 100644 index ec80bd6..0000000 --- a/AccountSwapper/content.js +++ /dev/null @@ -1,32 +0,0 @@ -window.addEventListener('message', (event) => { - if (event.source !== window) return; - if (event.data.source !== 'my-userscript') return; - - const message = event.data; - if (message.type === 'setCookie' && message.value) { - chrome.runtime.sendMessage( - { - type: 'setCookie', - value: message.value, - }, - (response) => { - if (response?.status === 'ok') { - console.log('✅ Forwarded token to background.'); - } - } - ); - } -}); - -chrome.runtime.onMessage.addListener((msg) => { - if (msg.type === 'cookieSet') { - window.postMessage( - { - source: 'my-extension', - type: 'cookieSet', - value: msg.value, - }, - '*' - ); - } -}); diff --git a/AccountSwapper/manifest.json b/AccountSwapper/manifest.json deleted file mode 100644 index 3de491e..0000000 --- a/AccountSwapper/manifest.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "manifest_version": 3, - "name": "WPlace Cookie Setter", - "version": "1.0.0", - "permissions": [ - "cookies", - "webRequest", - "scripting", - "tabs", - "storage", - "browsingData", - "webNavigation" - ], - "host_permissions": [ - "https://backend.wplace.live/*", - "https://wplace.live/*", - "https://*.wplace.live/*" - ], - "background": { "service_worker": "background.js" }, - "content_scripts": [ - { - "matches": ["https://wplace.live/*", "https://*.wplace.live/*"], - "js": ["content.js"], - "run_at": "document_idle" - } - ] -} From cbfd0e17e4b1ea10befc92f3328fd146b4a22d1f Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:19:13 +0800 Subject: [PATCH 03/18] Delete .gitignore --- .gitignore | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7ec2b31..0000000 --- a/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# Node modules -node_modules/ - -# NPM/Yarn lock -package-lock.json -package.json - -# Prettier -.prettierrc.json - -# ESLint -eslint.config.cjs - -# Staged changes -staged_changes.diff - -# IDE (WebStorm/IntelliJ IDEA) -.idea/ From 0abdb5d9439c40a0a9f4434aee8287d2f720a2c8 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:19:26 +0800 Subject: [PATCH 04/18] Delete auto-image-styles.css --- auto-image-styles.css | 1914 ----------------------------------------- 1 file changed, 1914 deletions(-) delete mode 100644 auto-image-styles.css diff --git a/auto-image-styles.css b/auto-image-styles.css deleted file mode 100644 index 95b1ffa..0000000 --- a/auto-image-styles.css +++ /dev/null @@ -1,1914 +0,0 @@ -/* 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; -} From 5df8c076422995316de22f34def8866054e1d82f Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:19:36 +0800 Subject: [PATCH 05/18] Delete Auto-Image.js --- Auto-Image.js | 7907 ------------------------------------------------- 1 file changed, 7907 deletions(-) delete mode 100644 Auto-Image.js diff --git a/Auto-Image.js b/Auto-Image.js deleted file mode 100644 index cbbc75d..0000000 --- a/Auto-Image.js +++ /dev/null @@ -1,7907 +0,0 @@ -// 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, // Minimum 1 pixel batch size - MAX: 1000, // Maximum 1000 pixels batch size - DEFAULT: 5, // Default 5 pixels batch size - }, - BATCH_MODE: 'normal', // "normal" or "random" - default to normal - RANDOM_BATCH_RANGE: { - MIN: 3, // Random range minimum - MAX: 20, // Random range maximum - }, - PAINTING_SPEED_ENABLED: true, // On by default - AUTO_CAPTCHA_ENABLED: true, // Turnstile generator enabled by default - TOKEN_SOURCE: 'generator', // "generator", "manual", or "hybrid" - default to generator - COOLDOWN_CHARGE_THRESHOLD: 1, // Default wait threshold - // Desktop Notifications (defaults) - NOTIFICATIONS: { - ENABLED: false, - ON_CHARGES_REACHED: true, - ONLY_WHEN_UNFOCUSED: true, - REPEAT_MINUTES: 5, // repeat reminder while threshold condition holds - }, - OVERLAY: { - OPACITY_DEFAULT: 0.2, - BLUE_MARBLE_DEFAULT: false, - ditheringEnabled: false, - }, // --- START: Color data from colour-converter.js --- - // New color structure with proper ID mapping - 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 }, - }, // --- END: Color data --- - // Optimized CSS Classes for reuse - CSS_CLASSES: { - BUTTON_PRIMARY: ` - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; border: none; border-radius: 8px; padding: 10px 16px; - cursor: pointer; font-weight: 500; transition: all 0.3s ease; - display: flex; align-items: center; gap: 8px; - `, - BUTTON_SECONDARY: ` - background: rgba(255,255,255,0.1); color: white; - border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; - padding: 8px 12px; cursor: pointer; transition: all 0.3s ease; - `, - MODERN_CARD: ` - background: rgba(255,255,255,0.1); border-radius: 12px; - padding: 18px; border: 1px solid rgba(255,255,255,0.1); - backdrop-filter: blur(5px); - `, - GRADIENT_TEXT: ` - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - -webkit-background-clip: text; -webkit-text-fill-color: transparent; - background-clip: text; font-weight: bold; - `, - }, - THEMES: { - 'Classic Autobot': { - primary: '#000000', - secondary: '#111111', - accent: '#222222', - text: '#ffffff', - highlight: '#775ce3', - success: '#00ff00', - error: '#ff0000', - warning: '#ffaa00', - fontFamily: "'Segoe UI', Roboto, sans-serif", - borderRadius: '12px', - borderStyle: 'solid', - borderWidth: '1px', - boxShadow: '0 8px 32px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.1)', - backdropFilter: 'blur(10px)', - animations: { - glow: false, - scanline: false, - 'pixel-blink': false, - }, - }, - 'Classic Light': { - primary: '#ffffff', - secondary: '#f8f9fa', - accent: '#e9ecef', - text: '#212529', - highlight: '#6f42c1', - success: '#28a745', - error: '#dc3545', - warning: '#ffc107', - fontFamily: "'Segoe UI', Roboto, sans-serif", - borderRadius: '12px', - borderStyle: 'solid', - borderWidth: '1px', - boxShadow: '0 8px 32px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.08)', - backdropFilter: 'blur(10px)', - animations: { - glow: false, - scanline: false, - 'pixel-blink': false, - }, - }, - 'Neon Retro': { - primary: '#1a1a2e', - secondary: '#16213e', - accent: '#0f3460', - text: '#00ff41', - highlight: '#ff6b35', - success: '#39ff14', - error: '#ff073a', - warning: '#ffff00', - neon: '#00ffff', - purple: '#bf00ff', - pink: '#ff1493', - fontFamily: "'Press Start 2P', monospace", - borderRadius: '0', - borderStyle: 'solid', - borderWidth: '3px', - boxShadow: '0 0 20px rgba(0, 255, 65, 0.3), inset 0 0 20px rgba(0, 255, 65, 0.1)', - backdropFilter: 'none', - animations: { - glow: true, - scanline: true, - 'pixel-blink': true, - }, - }, - }, - currentTheme: 'Classic Autobot', - PAINT_UNAVAILABLE: true, - COORDINATE_MODE: 'rows', - COORDINATE_DIRECTION: 'top-left', - COORDINATE_SNAKE: true, - COORDINATE_BLOCK_WIDTH: 6, - COORDINATE_BLOCK_HEIGHT: 2, - }; - - const getCurrentTheme = () => CONFIG.THEMES[CONFIG.currentTheme]; - - const switchTheme = (themeName) => { - if (CONFIG.THEMES[themeName]) { - CONFIG.currentTheme = themeName; - saveThemePreference(); - - // APPLY THEME VARS/CLASS (new) - applyTheme(); - - // Recreate UI (kept for now) - createUI(); - } - }; - - // Add this helper (place it after getCurrentTheme/switchTheme definitions) - function applyTheme() { - const theme = getCurrentTheme(); - // Toggle theme class on documentElement so CSS vars cascade to our UI - document.documentElement.classList.remove( - 'wplace-theme-classic', - 'wplace-theme-classic-light', - 'wplace-theme-neon' - ); - - let themeClass = 'wplace-theme-classic'; // default - if (CONFIG.currentTheme === 'Neon Retro') { - themeClass = 'wplace-theme-neon'; - } else if (CONFIG.currentTheme === 'Classic Light') { - themeClass = 'wplace-theme-classic-light'; - } - - document.documentElement.classList.add(themeClass); - - // Also set CSS variables explicitly in case you want runtime overrides - const root = document.documentElement; - const setVar = (k, v) => { - try { - root.style.setProperty(k, v); - } catch {} - }; - - setVar('--wplace-primary', theme.primary); - setVar('--wplace-secondary', theme.secondary); - setVar('--wplace-accent', theme.accent); - setVar('--wplace-text', theme.text); - setVar('--wplace-highlight', theme.highlight); - setVar('--wplace-success', theme.success); - setVar('--wplace-error', theme.error); - setVar('--wplace-warning', theme.warning); - - // Typography + look - setVar('--wplace-font', theme.fontFamily || "'Segoe UI', Roboto, sans-serif"); - setVar('--wplace-radius', '' + (theme.borderRadius || '12px')); - setVar('--wplace-border-style', '' + (theme.borderStyle || 'solid')); - setVar('--wplace-border-width', '' + (theme.borderWidth || '1px')); - setVar('--wplace-backdrop', '' + (theme.backdropFilter || 'blur(10px)')); - setVar('--wplace-border-color', 'rgba(255,255,255,0.1)'); - } - - const saveThemePreference = () => { - try { - localStorage.setItem('wplace-theme', CONFIG.currentTheme); - } catch (e) { - console.warn('Could not save theme preference:', e); - } - }; - - const loadThemePreference = () => { - try { - const saved = localStorage.getItem('wplace-theme'); - if (saved && CONFIG.THEMES[saved]) { - CONFIG.currentTheme = saved; - } - } catch (e) { - console.warn('Could not load theme preference:', e); - } - }; - - // Simple translation cache - const translationCache = new Map(); - - // Dynamically loaded translations - let loadedTranslations = {}; - - // Available languages - const AVAILABLE_LANGUAGES = [ - 'en', - 'ru', - 'pt', - 'vi', - 'fr', - 'id', - 'tr', - 'zh-CN', - 'zh-TW', - 'ja', - 'ko', - 'uk', - ]; - - // Function to load translations from JSON file with retry mechanism - const loadTranslations = async (language, retryCount = 0) => { - if (loadedTranslations[language]) { - return loadedTranslations[language]; - } - - // Load translations from CDN - const url = `https://wplace-autobot.github.io/WPlace-AutoBOT/main/lang/${language}.json`; - const maxRetries = 3; - const baseDelay = 1000; // 1 second - - try { - if (retryCount === 0) { - console.log(`🔄 Loading ${language} translations from CDN...`); - } else { - console.log( - `🔄 Retrying ${language} translations (attempt ${retryCount + 1}/${maxRetries + 1})...` - ); - } - - const response = await fetch(url); - if (response.ok) { - const translations = await response.json(); - - // Validate that translations is an object with keys - if ( - typeof translations === 'object' && - translations !== null && - Object.keys(translations).length > 0 - ) { - loadedTranslations[language] = translations; - console.log( - `📚 Loaded ${language} translations successfully from CDN (${ - Object.keys(translations).length - } keys)` - ); - return translations; - } else { - console.warn(`❌ Invalid translation format for ${language}`); - throw new Error('Invalid translation format'); - } - } else { - console.warn( - `❌ CDN returned HTTP ${response.status}: ${response.statusText} for ${language} translations` - ); - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - } catch (error) { - console.error( - `❌ Failed to load ${language} translations from CDN (attempt ${retryCount + 1}):`, - error - ); - - // Retry with exponential backoff - if (retryCount < maxRetries) { - const delay = baseDelay * Math.pow(2, retryCount); - console.log(`⏳ Retrying in ${delay}ms...`); - await new Promise((resolve) => setTimeout(resolve, delay)); - return loadTranslations(language, retryCount + 1); - } - } - - return null; - }; - - const loadLanguagePreference = async () => { - const savedLanguage = localStorage.getItem('wplace_language'); - const browserLocale = navigator.language; - const browserLanguage = browserLocale.split('-')[0]; - - let selectedLanguage = 'en'; // Default fallback - - try { - // Check if we have the saved language available - if (savedLanguage && AVAILABLE_LANGUAGES.includes(savedLanguage)) { - selectedLanguage = savedLanguage; - console.log(`🔄 Using saved language preference: ${selectedLanguage}`); - } - // Try full locale match (e.g. "zh-CN", "zh-TW" etc) - else if (AVAILABLE_LANGUAGES.includes(browserLocale)) { - selectedLanguage = browserLocale; - localStorage.setItem('wplace_language', browserLocale); - console.log(`🔄 Using browser locale: ${selectedLanguage}`); - } - // Try base language match (e.g. "en" for "en-US" or "en-GB" etc) - else if (AVAILABLE_LANGUAGES.includes(browserLanguage)) { - selectedLanguage = browserLanguage; - localStorage.setItem('wplace_language', browserLanguage); - console.log(`🔄 Using browser language: ${selectedLanguage}`); - } - // Use English as fallback - else { - console.log(`🔄 No matching language found, using English fallback`); - } - - // Set the language in state first - state.language = selectedLanguage; - - // Only load translations if not already loaded and not English (which should already be loaded) - if (selectedLanguage !== 'en' && !loadedTranslations[selectedLanguage]) { - const loaded = await loadTranslations(selectedLanguage); - if (!loaded) { - console.warn( - `⚠️ Failed to load ${selectedLanguage} translations, falling back to English` - ); - state.language = 'en'; - localStorage.setItem('wplace_language', 'en'); - } - } - } catch (error) { - console.error(`❌ Error in loadLanguagePreference:`, error); - state.language = 'en'; // Always ensure we have a valid language - } - }; - - // Simple user notification function for critical issues - const showTranslationWarning = (message) => { - try { - // Create a simple temporary notification banner - const warning = document.createElement('div'); - warning.style.cssText = ` - position: fixed; top: 10px; right: 10px; z-index: 10001; - background: rgba(255, 193, 7, 0.95); color: #212529; padding: 12px 16px; - border-radius: 8px; font-size: 14px; font-weight: 500; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); border: 1px solid rgba(255, 193, 7, 0.8); - max-width: 300px; word-wrap: break-word; - `; - warning.textContent = message; - document.body.appendChild(warning); - - // Auto-remove after 8 seconds - setTimeout(() => { - if (warning.parentNode) { - warning.remove(); - } - }, 8000); - } catch (e) { - // If DOM manipulation fails, just log - console.warn('Failed to show translation warning UI:', e); - } - }; - - // Initialize translations function - const initializeTranslations = async () => { - try { - console.log('🌐 Initializing translation system...'); - - // Always ensure English is loaded as fallback first - if (!loadedTranslations['en']) { - const englishLoaded = await loadTranslations('en'); - if (!englishLoaded) { - console.warn('⚠️ Failed to load English translations from CDN, using fallback'); - showTranslationWarning('⚠️ Translation loading failed, using basic fallbacks'); - } - } - - // Then load user's language preference - await loadLanguagePreference(); - - console.log(`✅ Translation system initialized. Active language: ${state.language}`); - } catch (error) { - console.error('❌ Translation initialization failed:', error); - // Ensure state has a valid language even if loading fails - if (!state.language) { - state.language = 'en'; - } - console.warn('⚠️ Using fallback translations due to initialization failure'); - showTranslationWarning('⚠️ Translation system error, using basic English'); - } - }; - - // Emergency fallback TEXT (minimal) - const FALLBACK_TEXT = { - en: { - title: 'WPlace Auto-Image', - toggleOverlay: 'Toggle Overlay', - scanColors: 'Scan Colors', - uploadImage: 'Upload Image', - resizeImage: 'Resize Image', - selectPosition: 'Select Position', - startPainting: 'Start Painting', - stopPainting: 'Stop Painting', - progress: 'Progress', - pixels: 'Pixels', - charges: 'Charges', - batchSize: 'Batch Size', - cooldownSettings: 'Cooldown Settings', - waitCharges: 'Wait for Charges', - settings: 'Settings', - showStats: 'Show Statistics', - compactMode: 'Compact Mode', - minimize: 'Minimize', - tokenCapturedSuccess: 'Token captured successfully', - turnstileInstructions: 'Complete the verification', - hideTurnstileBtn: 'Hide', - notificationsNotSupported: 'Notifications not supported', - chargesReadyMessage: 'Charges are ready', - chargesReadyNotification: 'WPlace AutoBot', - initMessage: "Click 'Upload Image' to begin", - }, - }; - - // Safe translation function with multiple fallback levels - const getText = (key, replacements = {}) => { - // Try current language first - let text = loadedTranslations[state.language]?.[key]; - - // Fallback to English translations - if (!text && state.language !== 'en') { - text = loadedTranslations['en']?.[key]; - } - - // Fallback to hardcoded English - if (!text) { - text = FALLBACK_TEXT['en']?.[key]; - } - - // Last resort - return the key itself - if (!text) { - console.warn(`⚠️ Missing translation for key: ${key}`); - return key; - } - - // Handle string replacements like {count}, {time}, etc. - return Object.entries(replacements).reduce((result, [placeholder, value]) => { - return result.replace(new RegExp(`\\{${placeholder}\\}`, 'g'), value); - }, text); - }; - - // GLOBAL STATE - const state = { - running: false, - imageLoaded: false, - processing: false, - totalPixels: 0, - paintedPixels: 0, - availableColors: [], - activeColorPalette: [], // User-selected colors for conversion - paintWhitePixels: true, // Default to ON - fullChargeData: null, - fullChargeInterval: null, - paintTransparentPixels: false, // Default to OFF - displayCharges: 0, - preciseCurrentCharges: 0, - maxCharges: 1, // Default max charges - cooldown: CONFIG.COOLDOWN_DEFAULT, - imageData: null, - stopFlag: false, - colorsChecked: false, - startPosition: null, - selectingPosition: false, - region: null, - minimized: false, - lastPosition: { x: 0, y: 0 }, - estimatedTime: 0, - language: 'en', - paintingSpeed: CONFIG.PAINTING_SPEED.DEFAULT, // pixels batch size - batchMode: CONFIG.BATCH_MODE, // "normal" or "random" - randomBatchMin: CONFIG.RANDOM_BATCH_RANGE.MIN, // Random range minimum - randomBatchMax: CONFIG.RANDOM_BATCH_RANGE.MAX, // Random range maximum - cooldownChargeThreshold: CONFIG.COOLDOWN_CHARGE_THRESHOLD, - chargesThresholdInterval: null, - tokenSource: CONFIG.TOKEN_SOURCE, // "generator" or "manual" - initialSetupComplete: false, // Track if initial startup setup is complete (only happens once) - overlayOpacity: CONFIG.OVERLAY.OPACITY_DEFAULT, - blueMarbleEnabled: CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT, - ditheringEnabled: true, - // Advanced color matching settings - colorMatchingAlgorithm: 'lab', - enableChromaPenalty: true, - chromaPenaltyWeight: 0.15, - customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD, - customWhiteThreshold: CONFIG.WHITE_THRESHOLD, - resizeSettings: null, - originalImage: null, - resizeIgnoreMask: null, - paintUnavailablePixels: CONFIG.PAINT_UNAVAILABLE, - // Coordinate generation settings - coordinateMode: CONFIG.COORDINATE_MODE, - coordinateDirection: CONFIG.COORDINATE_DIRECTION, - coordinateSnake: CONFIG.COORDINATE_SNAKE, - blockWidth: CONFIG.COORDINATE_BLOCK_WIDTH, - blockHeight: CONFIG.COORDINATE_BLOCK_HEIGHT, - notificationsEnabled: CONFIG.NOTIFICATIONS.ENABLED, - notifyOnChargesReached: CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED, - notifyOnlyWhenUnfocused: CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED, - notificationIntervalMinutes: CONFIG.NOTIFICATIONS.REPEAT_MINUTES, - _lastChargesNotifyAt: 0, - _lastChargesBelow: true, - // Smart save tracking - _lastSavePixelCount: 0, - _lastSaveTime: 0, - _saveInProgress: false, - paintedMap: null, - }; - - let _updateResizePreview = () => {}; - let _resizeDialogCleanup = null; - - // --- OVERLAY UPDATE: Optimized OverlayManager class with performance improvements --- - class OverlayManager { - constructor() { - this.isEnabled = false; - this.startCoords = null; // { region: {x, y}, pixel: {x, y} } - this.imageBitmap = null; - this.chunkedTiles = new Map(); // Map<"tileX,tileY", ImageBitmap> - this.originalTiles = new Map(); // Map<"tileX,tileY", ImageBitmap> store latest original tile bitmaps - this.originalTilesData = new Map(); // Map<"tileX,tileY", {w,h,data:Uint8ClampedArray}> cache full ImageData for fast pixel reads - this.tileSize = 1000; - this.processPromise = null; // Track ongoing processing - this.lastProcessedHash = null; // Cache invalidation - this.workerPool = null; // Web worker pool for heavy processing - } - - toggle() { - this.isEnabled = !this.isEnabled; - console.log(`Overlay ${this.isEnabled ? 'enabled' : 'disabled'}.`); - return this.isEnabled; - } - - enable() { - this.isEnabled = true; - } - - disable() { - this.isEnabled = false; - } - - clear() { - this.disable(); - this.imageBitmap = null; - this.chunkedTiles.clear(); - this.originalTiles.clear(); - this.originalTilesData.clear(); - this.lastProcessedHash = null; - if (this.processPromise) { - this.processPromise = null; - } - } - - async setImage(imageBitmap) { - this.imageBitmap = imageBitmap; - this.lastProcessedHash = null; // Invalidate cache - if (this.imageBitmap && this.startCoords) { - await this.processImageIntoChunks(); - } - } - - async setPosition(startPosition, region) { - if (!startPosition || !region) { - this.startCoords = null; - this.chunkedTiles.clear(); - this.lastProcessedHash = null; - return; - } - this.startCoords = { region, pixel: startPosition }; - this.lastProcessedHash = null; // Invalidate cache - if (this.imageBitmap) { - await this.processImageIntoChunks(); - } - } - - // Generate hash for cache invalidation - _generateProcessHash() { - if (!this.imageBitmap || !this.startCoords) return null; - const { width, height } = this.imageBitmap; - const { x: px, y: py } = this.startCoords.pixel; - const { x: rx, y: ry } = this.startCoords.region; - return `${width}x${height}_${px},${py}_${rx},${ry}_${state.blueMarbleEnabled}_${state.overlayOpacity}`; - } - - // --- OVERLAY UPDATE: Optimized chunking with caching and batch processing --- - async processImageIntoChunks() { - if (!this.imageBitmap || !this.startCoords) return; - - // Check if we're already processing to avoid duplicate work - if (this.processPromise) { - return this.processPromise; - } - - // Check cache validity - const currentHash = this._generateProcessHash(); - if (this.lastProcessedHash === currentHash && this.chunkedTiles.size > 0) { - console.log(`📦 Using cached overlay chunks (${this.chunkedTiles.size} tiles)`); - return; - } - - // Start processing - this.processPromise = this._doProcessImageIntoChunks(); - try { - await this.processPromise; - this.lastProcessedHash = currentHash; - } finally { - this.processPromise = null; - } - } - - async _doProcessImageIntoChunks() { - const startTime = performance.now(); - this.chunkedTiles.clear(); - - const { width: imageWidth, height: imageHeight } = this.imageBitmap; - const { x: startPixelX, y: startPixelY } = this.startCoords.pixel; - const { x: startRegionX, y: startRegionY } = this.startCoords.region; - - const { startTileX, startTileY, endTileX, endTileY } = Utils.calculateTileRange( - startRegionX, - startRegionY, - startPixelX, - startPixelY, - imageWidth, - imageHeight, - this.tileSize - ); - - const totalTiles = (endTileX - startTileX + 1) * (endTileY - startTileY + 1); - console.log(`🔄 Processing ${totalTiles} overlay tiles...`); - - // Process tiles in batches to avoid blocking the main thread - const batchSize = 4; // Process 4 tiles at a time - const tilesToProcess = []; - - for (let ty = startTileY; ty <= endTileY; ty++) { - for (let tx = startTileX; tx <= endTileX; tx++) { - tilesToProcess.push({ tx, ty }); - } - } - - // Process tiles in batches with yielding - for (let i = 0; i < tilesToProcess.length; i += batchSize) { - const batch = tilesToProcess.slice(i, i + batchSize); - - await Promise.all( - batch.map(async ({ tx, ty }) => { - const tileKey = `${tx},${ty}`; - const chunkBitmap = await this._processTile( - tx, - ty, - imageWidth, - imageHeight, - startPixelX, - startPixelY, - startRegionX, - startRegionY - ); - if (chunkBitmap) { - this.chunkedTiles.set(tileKey, chunkBitmap); - } - }) - ); - - // Yield control to prevent blocking - if (i + batchSize < tilesToProcess.length) { - await new Promise((resolve) => setTimeout(resolve, 0)); - } - } - - const processingTime = performance.now() - startTime; - console.log( - `✅ Overlay processed ${this.chunkedTiles.size} tiles in ${Math.round(processingTime)}ms` - ); - } - - async _processTile( - tx, - ty, - imageWidth, - imageHeight, - startPixelX, - startPixelY, - startRegionX, - startRegionY - ) { - const tileKey = `${tx},${ty}`; - - // Calculate the portion of the image that overlaps with this tile - const imgStartX = (tx - startRegionX) * this.tileSize - startPixelX; - const imgStartY = (ty - startRegionY) * this.tileSize - startPixelY; - - // Crop coordinates within the source image - const sX = Math.max(0, imgStartX); - const sY = Math.max(0, imgStartY); - const sW = Math.min(imageWidth - sX, this.tileSize - (sX - imgStartX)); - const sH = Math.min(imageHeight - sY, this.tileSize - (sY - imgStartY)); - - if (sW <= 0 || sH <= 0) return null; - - // Destination coordinates on the new chunk canvas - const dX = Math.max(0, -imgStartX); - const dY = Math.max(0, -imgStartY); - - const chunkCanvas = new OffscreenCanvas(this.tileSize, this.tileSize); - const chunkCtx = chunkCanvas.getContext('2d'); - chunkCtx.imageSmoothingEnabled = false; - - chunkCtx.drawImage(this.imageBitmap, sX, sY, sW, sH, dX, dY, sW, sH); - - // --- OPTIMIZED: Blue marble effect with faster pixel manipulation --- - if (state.blueMarbleEnabled) { - const imageData = chunkCtx.getImageData(dX, dY, sW, sH); - const data = imageData.data; - - // Faster pixel manipulation using typed arrays - for (let i = 0; i < data.length; i += 4) { - const pixelIndex = i / 4; - const pixelY = Math.floor(pixelIndex / sW); - const pixelX = pixelIndex % sW; - - if ((pixelX + pixelY) % 2 === 0 && data[i + 3] > 0) { - data[i + 3] = 0; // Set alpha to 0 - } - } - - chunkCtx.putImageData(imageData, dX, dY); - } - - return await chunkCanvas.transferToImageBitmap(); - } - - // --- OVERLAY UPDATE: Optimized compositing with caching --- - async processAndRespondToTileRequest(eventData) { - const { endpoint, blobID, blobData } = eventData; - - let finalBlob = blobData; - - if (this.isEnabled && this.chunkedTiles.size > 0) { - const tileMatch = endpoint.match(/(\d+)\/(\d+)\.png/); - if (tileMatch) { - const tileX = parseInt(tileMatch[1], 10); - const tileY = parseInt(tileMatch[2], 10); - const tileKey = `${tileX},${tileY}`; - - const chunkBitmap = this.chunkedTiles.get(tileKey); - // Also store the original tile bitmap for later pixel color checks - try { - const originalBitmap = await createImageBitmap(blobData); - this.originalTiles.set(tileKey, originalBitmap); - // Cache full ImageData for fast pixel access (avoid repeated drawImage/getImageData) - try { - let canvas, ctx; - if (typeof OffscreenCanvas !== 'undefined') { - canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height); - ctx = canvas.getContext('2d'); - } else { - canvas = document.createElement('canvas'); - canvas.width = originalBitmap.width; - canvas.height = originalBitmap.height; - ctx = canvas.getContext('2d'); - } - ctx.imageSmoothingEnabled = false; - ctx.drawImage(originalBitmap, 0, 0); - const imgData = ctx.getImageData(0, 0, originalBitmap.width, originalBitmap.height); - // Store typed array copy to avoid retaining large canvas - this.originalTilesData.set(tileKey, { - w: originalBitmap.width, - h: originalBitmap.height, - data: new Uint8ClampedArray(imgData.data), - }); - } catch (e) { - // If ImageData extraction fails, still keep the bitmap as fallback - console.warn('OverlayManager: could not cache ImageData for', tileKey, e); - } - } catch (e) { - console.warn('OverlayManager: could not create original bitmap for', tileKey, e); - } - if (chunkBitmap) { - try { - // Use faster compositing for better performance - finalBlob = await this._compositeTileOptimized(blobData, chunkBitmap); - } catch (e) { - console.error('Error compositing overlay:', e); - // Fallback to original tile on error - finalBlob = blobData; - } - } - } - } - - // Send the (possibly modified) blob back to the injected script - window.postMessage( - { - source: 'auto-image-overlay', - blobID: blobID, - blobData: finalBlob, - }, - '*' - ); - } - - // Returns [r,g,b,a] for a pixel inside a region tile (tileX, tileY are region coords) - async getTilePixelColor(tileX, tileY, pixelX, pixelY) { - const tileKey = `${tileX},${tileY}`; - const alphaThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - - // 1. Prefer cached ImageData if available - const cached = this.originalTilesData.get(tileKey); - if (cached && cached.data && cached.w > 0 && cached.h > 0) { - const x = Math.max(0, Math.min(cached.w - 1, pixelX)); - const y = Math.max(0, Math.min(cached.h - 1, pixelY)); - const idx = (y * cached.w + x) * 4; - const d = cached.data; - const a = d[idx + 3]; - - if (!state.paintTransparentPixels && a < alphaThresh) { - // Treat as transparent / unavailable - // Lightweight debug: show when transparency causes skip (only if verbose enabled) - if (window._overlayDebug) - console.debug('OverlayManager: pixel transparent (cached), skipping', tileKey, x, y, a); - return null; - } - return [d[idx], d[idx + 1], d[idx + 2], a]; - } - - // 2. Fallback: use bitmap, with retry - const maxRetries = 3; - for (let attempt = 1; attempt <= maxRetries; attempt++) { - const bitmap = this.originalTiles.get(tileKey); - if (!bitmap) { - if (attempt === maxRetries) { - console.warn('OverlayManager: no bitmap for', tileKey, 'after', maxRetries, 'attempts'); - } else { - await Utils.sleep(50 * attempt); // exponential delay - } - continue; - } - - try { - let canvas, ctx; - if (typeof OffscreenCanvas !== 'undefined') { - canvas = new OffscreenCanvas(bitmap.width, bitmap.height); - ctx = canvas.getContext('2d'); - } else { - canvas = document.createElement('canvas'); - canvas.width = bitmap.width; - canvas.height = bitmap.height; - ctx = canvas.getContext('2d'); - } - ctx.imageSmoothingEnabled = false; - ctx.drawImage(bitmap, 0, 0); - - const x = Math.max(0, Math.min(bitmap.width - 1, pixelX)); - const y = Math.max(0, Math.min(bitmap.height - 1, pixelY)); - const data = ctx.getImageData(x, y, 1, 1).data; - const a = data[3]; - - if (!state.paintTransparentPixels && a < alphaThresh) { - if (window._overlayDebug) - console.debug('OverlayManager: pixel transparent (fallback)', tileKey, x, y, a); - return null; - } - - return [data[0], data[1], data[2], a]; - } catch (e) { - console.warn('OverlayManager: failed to read pixel (attempt', attempt, ')', tileKey, e); - if (attempt < maxRetries) { - await Utils.sleep(50 * attempt); - } else { - console.error( - 'OverlayManager: failed to read pixel after', - maxRetries, - 'attempts', - tileKey - ); - } - } - } - - // 3. If everything fails — you can return null or [0,0,0,0] - // Prefer null — to avoid misleading - return null; - } - - async _compositeTileOptimized(originalBlob, overlayBitmap) { - const originalBitmap = await createImageBitmap(originalBlob); - const canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height); - const ctx = canvas.getContext('2d'); - - // Disable antialiasing for pixel-perfect rendering - ctx.imageSmoothingEnabled = false; - - // Draw original tile first - ctx.drawImage(originalBitmap, 0, 0); - - // Set opacity and draw overlay with optimized blend mode - ctx.globalAlpha = state.overlayOpacity; - ctx.globalCompositeOperation = 'source-over'; - ctx.drawImage(overlayBitmap, 0, 0); - - // Use faster blob conversion with compression settings - return await canvas.convertToBlob({ - type: 'image/png', - quality: 0.95, // Slight compression for faster processing - }); - } - - /** - * Wait until all required tiles are loaded and cached - * @param {number} startRegionX - * @param {number} startRegionY - * @param {number} pixelWidth - * @param {number} pixelHeight - * @param {number} startPixelX - * @param {number} startPixelY - * @param {number} timeoutMs - * @returns {Promise} true if tiles are ready - */ - async waitForTiles( - startRegionX, - startRegionY, - pixelWidth, - pixelHeight, - startPixelX = 0, - startPixelY = 0, - timeoutMs = 10000 - ) { - const { startTileX, startTileY, endTileX, endTileY } = Utils.calculateTileRange( - startRegionX, - startRegionY, - startPixelX, - startPixelY, - pixelWidth, - pixelHeight, - this.tileSize - ); - - const requiredTiles = []; - for (let ty = startTileY; ty <= endTileY; ty++) { - for (let tx = startTileX; tx <= endTileX; tx++) { - requiredTiles.push(`${tx},${ty}`); - } - } - - if (requiredTiles.length === 0) return true; - - const startTime = Date.now(); - - while (Date.now() - startTime < timeoutMs) { - if (state.stopFlag) { - console.log('waitForTiles: stopped by user'); - return false; - } - - const missing = requiredTiles.filter((key) => !this.originalTiles.has(key)); - if (missing.length === 0) { - console.log(`✅ All ${requiredTiles.length} required tiles are loaded`); - return true; - } - - await Utils.sleep(100); - } - - console.warn(`❌ Timeout waiting for tiles: ${requiredTiles.length} required, - ${requiredTiles.filter((k) => this.originalTiles.has(k)).length} loaded`); - return false; - } - } - - const overlayManager = new OverlayManager(); - - // Optimized Turnstile token handling with improved caching and retry logic - 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; // Maximum attempts for batch sending - 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'); - } - - function isTokenValid() { - return turnstileToken && Date.now() < tokenExpiryTime; - } - - function invalidateToken() { - turnstileToken = null; - tokenExpiryTime = 0; - console.log('🗑️ Token invalidated, will force fresh generation'); - } - - 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() { - // Implementation for fallback token generation would go here - // This is a placeholder for browser automation fallback - console.log('🔄 Attempting fallback token generation...'); - return null; - } - - 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); - if (document.querySelector('#statusText')?.textContent.includes('CAPTCHA')) { - Utils.showAlert(Utils.t('tokenCapturedSuccess'), 'success'); - updateUI('colorsFound', 'success', { count: state.availableColors.length }); - } - } - }); - - async function detectLanguage() { - try { - const response = await fetch('https://backend.wplace.live/me', { - credentials: 'include', - }); - const data = await response.json(); - state.language = data.language === 'pt' ? 'pt' : 'en'; - } catch { - state.language = navigator.language.startsWith('pt') ? 'pt' : 'en'; - } - } - - // UTILITY FUNCTIONS - const Utils = { - sleep: (ms) => new Promise((r) => setTimeout(r, ms)), - - dynamicSleep: async function (tickAndGetRemainingMs) { - let remaining = Math.max(0, await tickAndGetRemainingMs()); - while (remaining > 0) { - const interval = remaining > 5000 ? 2000 : remaining > 1000 ? 500 : 100; - await this.sleep(Math.min(interval, remaining)); - remaining = Math.max(0, await tickAndGetRemainingMs()); - } - }, - - waitForSelector: async (selector, interval = 200, timeout = 5000) => { - const start = Date.now(); - while (Date.now() - start < timeout) { - const el = document.querySelector(selector); - if (el) return el; - await Utils.sleep(interval); - } - return null; - }, - - msToTimeText(ms) { - const totalSeconds = Math.ceil(ms / 1000); - const hours = Math.floor(totalSeconds / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const seconds = totalSeconds % 60; - - if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`; - if (minutes > 0) return `${minutes}m ${seconds}s`; - return `${seconds}s`; - }, - - // Debounced scroll-to-adjust handler for sliders - createScrollToAdjust: (element, updateCallback, min, max, step = 1) => { - let debounceTimer = null; - - const handleWheel = (e) => { - // Only trigger when hovering over the slider - if (e.target !== element) return; - - e.preventDefault(); - e.stopPropagation(); - - // Clear existing debounce timer - if (debounceTimer) { - clearTimeout(debounceTimer); - } - - // Debounce the adjustment to make it precise - debounceTimer = setTimeout(() => { - const currentValue = parseInt(element.value) || 0; - const delta = e.deltaY > 0 ? -step : step; - const newValue = Math.max(min, Math.min(max, currentValue + delta)); - - if (newValue !== currentValue) { - element.value = newValue; - updateCallback(newValue); - } - }, 50); // 50ms debounce for precise control - }; - - element.addEventListener('wheel', handleWheel, { passive: false }); - - // Return cleanup function - return () => { - if (debounceTimer) clearTimeout(debounceTimer); - element.removeEventListener('wheel', handleWheel); - }; - }, - - /** - * Calculate the range of tile coordinates (in region space) that cover a given image area. - * @param {number} startRegionX - Base region X - * @param {number} startRegionY - Base region Y - * @param {number} startPixelX - Starting pixel X within the region grid - * @param {number} startPixelY - Starting pixel Y within the region grid - * @param {number} width - Image width in pixels - * @param {number} height - Image height in pixels - * @param {number} tileSize - Size of a tile (default 1000) - * @returns {{ startTileX: number, startTileY: number, endTileX: number, endTileY: number }} - */ - calculateTileRange( - startRegionX, - startRegionY, - startPixelX, - startPixelY, - width, - height, - tileSize = 1000 - ) { - const endPixelX = startPixelX + width; - const endPixelY = startPixelY + height; - - return { - startTileX: startRegionX + Math.floor(startPixelX / tileSize), - startTileY: startRegionY + Math.floor(startPixelY / tileSize), - endTileX: startRegionX + Math.floor((endPixelX - 1) / tileSize), - endTileY: startRegionY + Math.floor((endPixelY - 1) / tileSize), - }; - }, // Turnstile Generator Integration - Optimized with widget reuse and proper cleanup - turnstileLoaded: false, - _turnstileContainer: null, - _turnstileOverlay: null, - _turnstileWidgetId: null, - _lastSitekey: null, - - async loadTurnstile() { - // If Turnstile is already present, just resolve. - if (window.turnstile) { - this.turnstileLoaded = true; - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - // Avoid adding the script twice - if ( - document.querySelector( - 'script[src^="https://challenges.cloudflare.com/turnstile/v0/api.js"]' - ) - ) { - const checkReady = () => { - if (window.turnstile) { - this.turnstileLoaded = true; - resolve(); - } else { - setTimeout(checkReady, 100); - } - }; - return checkReady(); - } - - const script = document.createElement('script'); - script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; - script.async = true; - script.defer = true; - script.onload = () => { - this.turnstileLoaded = true; - console.log('✅ Turnstile script loaded successfully'); - resolve(); - }; - script.onerror = () => { - console.error('❌ Failed to load Turnstile script'); - reject(new Error('Failed to load Turnstile')); - }; - document.head.appendChild(script); - }); - }, - - // Create or reuse the turnstile container - completely hidden for token generation - ensureTurnstileContainer() { - if (!this._turnstileContainer || !document.body.contains(this._turnstileContainer)) { - // Clean up old container if it exists - if (this._turnstileContainer) { - this._turnstileContainer.remove(); - } - - this._turnstileContainer = document.createElement('div'); - this._turnstileContainer.className = 'wplace-turnstile-hidden'; - this._turnstileContainer.setAttribute('aria-hidden', 'true'); - this._turnstileContainer.id = 'turnstile-widget-container'; - document.body.appendChild(this._turnstileContainer); - } - return this._turnstileContainer; - }, - - // Interactive overlay container for visible widgets when needed - ensureTurnstileOverlayContainer() { - if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { - return this._turnstileOverlay; - } - - const overlay = document.createElement('div'); - overlay.id = 'turnstile-overlay-container'; - overlay.className = 'wplace-turnstile-overlay wplace-overlay-hidden'; - - const title = document.createElement('div'); - title.textContent = Utils.t('turnstileInstructions'); - title.className = 'wplace-turnstile-title'; - - const host = document.createElement('div'); - host.id = 'turnstile-overlay-host'; - host.className = 'wplace-turnstile-host'; - - const hideBtn = document.createElement('button'); - hideBtn.textContent = Utils.t('hideTurnstileBtn'); - hideBtn.className = 'wplace-turnstile-hide-btn'; - hideBtn.addEventListener('click', () => overlay.remove()); - - overlay.appendChild(title); - overlay.appendChild(host); - overlay.appendChild(hideBtn); - document.body.appendChild(overlay); - - this._turnstileOverlay = overlay; - return overlay; - }, - - async executeTurnstile(sitekey, action = 'paint') { - await this.loadTurnstile(); - - // Try reusing existing widget first if sitekey matches - if (this._turnstileWidgetId && this._lastSitekey === sitekey && window.turnstile?.execute) { - try { - console.log('🔄 Reusing existing Turnstile widget...'); - const token = await Promise.race([ - window.turnstile.execute(this._turnstileWidgetId, { action }), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Execute timeout')), 15000) - ), - ]); - if (token && token.length > 20) { - console.log('✅ Token generated via widget reuse'); - return token; - } - } catch (error) { - console.log('� Widget reuse failed, will create a fresh widget:', error.message); - } - } - - // Try invisible widget first - const invisibleToken = await this.createTurnstileWidget(sitekey, action); - if (invisibleToken && invisibleToken.length > 20) { - return invisibleToken; - } - - console.log('� Falling back to interactive Turnstile (visible).'); - return await this.createTurnstileWidgetInteractive(sitekey, action); - }, - - async createTurnstileWidget(sitekey, action) { - return new Promise((resolve) => { - try { - // Force cleanup of any existing widget - if (this._turnstileWidgetId && window.turnstile?.remove) { - try { - window.turnstile.remove(this._turnstileWidgetId); - console.log('🧹 Cleaned up existing Turnstile widget'); - } catch (e) { - console.warn('⚠️ Widget cleanup warning:', e.message); - } - } - - const container = this.ensureTurnstileContainer(); - container.innerHTML = ''; - - // Verify Turnstile is available - if (!window.turnstile?.render) { - console.error('❌ Turnstile not available for rendering'); - resolve(null); - return; - } - - console.log('🔧 Creating invisible Turnstile widget...'); - const widgetId = window.turnstile.render(container, { - sitekey, - action, - size: 'invisible', - retry: 'auto', - 'retry-interval': 8000, - callback: (token) => { - console.log('✅ Invisible Turnstile callback'); - resolve(token); - }, - 'error-callback': () => resolve(null), - 'timeout-callback': () => resolve(null), - }); - - this._turnstileWidgetId = widgetId; - this._lastSitekey = sitekey; - - if (!widgetId) { - return resolve(null); - } - - // Execute the widget and race with timeout - Promise.race([ - window.turnstile.execute(widgetId, { action }), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Invisible execute timeout')), 12000) - ), - ]) - .then(resolve) - .catch(() => resolve(null)); - } catch (e) { - console.error('❌ Invisible Turnstile creation failed:', e); - resolve(null); - } - }); - }, - - async createTurnstileWidgetInteractive(sitekey, action) { - // Create a visible widget that users can interact with if needed - console.log('🔄 Creating interactive Turnstile widget (visible)'); - - return new Promise((resolve) => { - try { - // Force cleanup of any existing widget - if (this._turnstileWidgetId && window.turnstile?.remove) { - try { - window.turnstile.remove(this._turnstileWidgetId); - } catch (e) { - console.warn('⚠️ Widget cleanup warning:', e.message); - } - } - - const overlay = this.ensureTurnstileOverlayContainer(); - overlay.classList.remove('wplace-overlay-hidden'); - overlay.style.display = 'block'; - - const host = overlay.querySelector('#turnstile-overlay-host'); - host.innerHTML = ''; - - // Set a timeout for interactive mode - const timeout = setTimeout(() => { - console.warn('⏰ Interactive Turnstile widget timeout'); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; - resolve(null); - }, 60000); // 60 seconds for user interaction - - const widgetId = window.turnstile.render(host, { - sitekey, - action, - size: 'normal', - theme: 'light', - callback: (token) => { - clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; - console.log('✅ Interactive Turnstile completed successfully'); - - if (typeof token === 'string' && token.length > 20) { - resolve(token); - } else { - console.warn('❌ Invalid token from interactive widget'); - resolve(null); - } - }, - 'error-callback': (error) => { - clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; - console.warn('❌ Interactive Turnstile error:', error); - resolve(null); - }, - }); - - this._turnstileWidgetId = widgetId; - this._lastSitekey = sitekey; - - if (!widgetId) { - clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; - console.warn('❌ Failed to create interactive Turnstile widget'); - resolve(null); - } else { - console.log('✅ Interactive Turnstile widget created, waiting for user interaction...'); - } - } catch (e) { - console.error('❌ Interactive Turnstile creation failed:', e); - resolve(null); - } - }); - }, - - // Cleanup method for when the script is disabled/reloaded - cleanupTurnstile() { - if (this._turnstileWidgetId && window.turnstile?.remove) { - try { - window.turnstile.remove(this._turnstileWidgetId); - } catch (e) { - console.warn('Failed to cleanup Turnstile widget:', e); - } - } - - if (this._turnstileContainer && document.body.contains(this._turnstileContainer)) { - this._turnstileContainer.remove(); - } - - if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { - this._turnstileOverlay.remove(); - } - - this._turnstileWidgetId = null; - this._turnstileContainer = null; - this._turnstileOverlay = null; - this._lastSitekey = null; - }, - - async obtainSitekeyAndToken(fallback = '0x4AAAAAABpqJe8FO0N84q0F') { - // Cache sitekey to avoid repeated DOM queries - if (this._cachedSitekey) { - console.log('🔍 Using cached sitekey:', this._cachedSitekey); - - return isTokenValid() - ? { - sitekey: this._cachedSitekey, - token: turnstileToken, - } - : { sitekey: this._cachedSitekey, token: null }; - } - - // List of potential sitekeys to try - const potentialSitekeys = [ - '0x4AAAAAABpqJe8FO0N84q0F', // WPlace common sitekey - '0x4AAAAAAAJ7xjKAp6Mt_7zw', // Alternative WPlace sitekey - '0x4AAAAAADm5QWx6Ov2LNF2g', // Another common sitekey - ]; - const trySitekey = async (sitekey, source) => { - if (!sitekey || sitekey.length < 10) return null; - - console.log(`🔍 Testing sitekey from ${source}:`, sitekey); - const token = await this.executeTurnstile(sitekey); - - if (token && token.length >= 20) { - console.log(`✅ Valid token generated from ${source} sitekey`); - setTurnstileToken(token); - this._cachedSitekey = sitekey; - return { sitekey, token }; - } else { - console.log(`❌ Failed to get token from ${source} sitekey`); - return null; - } - }; - - try { - // 1️⃣ data-sitekey attribute - const sitekeySel = document.querySelector('[data-sitekey]'); - if (sitekeySel) { - const sitekey = sitekeySel.getAttribute('data-sitekey'); - const result = await trySitekey(sitekey, 'data attribute'); - if (result) { - return result; - } - } - - // 2️⃣ Turnstile element - const turnstileEl = document.querySelector('.cf-turnstile'); - if (turnstileEl?.dataset?.sitekey) { - const sitekey = turnstileEl.dataset.sitekey; - const result = await trySitekey(sitekey, 'turnstile element'); - if (result) { - return result; - } - } - - // 3️⃣ Meta tags - const metaTags = document.querySelectorAll( - 'meta[name*="turnstile"], meta[property*="turnstile"]' - ); - for (const meta of metaTags) { - const content = meta.getAttribute('content'); - const result = await trySitekey(content, 'meta tag'); - if (result) { - return result; - } - } - - // 4️⃣ Global variable - if (window.__TURNSTILE_SITEKEY) { - const result = await trySitekey(window.__TURNSTILE_SITEKEY, 'global variable'); - if (result) { - return result; - } - } - - // 5️⃣ Script tags - const scripts = document.querySelectorAll('script'); - for (const script of scripts) { - const content = script.textContent || script.innerHTML; - const match = content.match( - /(?:sitekey|data-sitekey)['"\s\[\]:\=\(]*['"]?([0-9a-zA-Z_-]{20,})['"]?/i - ); - if (match && match[1]) { - const extracted = match[1].replace(/['"]/g, ''); - const result = await trySitekey(extracted, 'script content'); - if (result) { - return result; - } - } - } - - // 6️⃣ Known potential sitekeys - console.log('🔍 Testing known potential sitekeys...'); - for (const testSitekey of potentialSitekeys) { - const result = await trySitekey(testSitekey, 'known list'); - if (result) { - return result; - } - } - } catch (error) { - console.warn('⚠️ Error during sitekey detection:', error); - } - - // 7️⃣ Fallback - console.log('🔧 Trying fallback sitekey:', fallback); - const fallbackResult = await trySitekey(fallback, 'fallback'); - if (fallbackResult) { - return fallbackResult; - } - - console.error('❌ No working sitekey or token found.'); - return { sitekey: null, token: null }; - }, - - createElement: (tag, props = {}, children = []) => { - const element = document.createElement(tag); - - Object.entries(props).forEach(([key, value]) => { - if (key === 'style' && typeof value === 'object') { - Object.assign(element.style, value); - } else if (key === 'className') { - element.className = value; - } else if (key === 'innerHTML') { - element.innerHTML = value; - } else { - element.setAttribute(key, value); - } - }); - - if (typeof children === 'string') { - element.textContent = children; - } else if (Array.isArray(children)) { - children.forEach((child) => { - if (typeof child === 'string') { - element.appendChild(document.createTextNode(child)); - } else { - element.appendChild(child); - } - }); - } - - return element; - }, - - createButton: (id, text, icon, onClick, style = CONFIG.CSS_CLASSES.BUTTON_PRIMARY) => { - const button = Utils.createElement('button', { - id: id, - style: style, - innerHTML: `${icon ? `` : ''}${text}`, - }); - if (onClick) button.addEventListener('click', onClick); - return button; - }, - - // Synchronous translation function for UI rendering - t: (key, params = {}) => { - // Try to get from cache first - const cacheKey = `${state.language}_${key}`; - if (translationCache.has(cacheKey)) { - let text = translationCache.get(cacheKey); - Object.keys(params).forEach((param) => { - text = text.replace(`{${param}}`, params[param]); - }); - return text; - } - - // Try dynamically loaded translations (already loaded) - if (loadedTranslations[state.language]?.[key]) { - let text = loadedTranslations[state.language][key]; - // Cache for future use - translationCache.set(cacheKey, text); - Object.keys(params).forEach((param) => { - text = text.replace(`{${param}}`, params[param]); - }); - return text; - } - - // Fallback to English if current language failed - if (state.language !== 'en' && loadedTranslations['en']?.[key]) { - let text = loadedTranslations['en'][key]; - Object.keys(params).forEach((param) => { - text = text.replace(`{${param}}`, params[param]); - }); - return text; - } - - // Final fallback to emergency fallback or key - let text = FALLBACK_TEXT[state.language]?.[key] || FALLBACK_TEXT.en?.[key] || key; - Object.keys(params).forEach((param) => { - text = text.replace(new RegExp(`\\{${param}\\}`, 'g'), params[param]); - }); - - // Log missing translations for debugging - if (text === key && key !== 'undefined') { - console.warn(`⚠️ Missing translation for key: ${key} (language: ${state.language})`); - } - - return text; - }, - - showAlert: (message, type = 'info') => { - const alertDiv = document.createElement('div'); - alertDiv.className = `wplace-alert-base wplace-alert-${type}`; - - alertDiv.textContent = message; - document.body.appendChild(alertDiv); - - setTimeout(() => { - alertDiv.style.animation = 'slide-down 0.3s ease-out reverse'; - setTimeout(() => { - document.body.removeChild(alertDiv); - }, 300); - }, 4000); - }, - - colorDistance: (a, b) => - Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2)), - _labCache: new Map(), // key: (r<<16)|(g<<8)|b value: [L,a,b] - _rgbToLab: (r, g, b) => { - // sRGB -> linear - const srgbToLinear = (v) => { - v /= 255; - return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); - }; - const rl = srgbToLinear(r); - const gl = srgbToLinear(g); - const bl = srgbToLinear(b); - let X = rl * 0.4124 + gl * 0.3576 + bl * 0.1805; - let Y = rl * 0.2126 + gl * 0.7152 + bl * 0.0722; - let Z = rl * 0.0193 + gl * 0.1192 + bl * 0.9505; - X /= 0.95047; - Y /= 1.0; - Z /= 1.08883; - const f = (t) => (t > 0.008856 ? Math.cbrt(t) : 7.787 * t + 16 / 116); - const fX = f(X), - fY = f(Y), - fZ = f(Z); - const L = 116 * fY - 16; - const a = 500 * (fX - fY); - const b2 = 200 * (fY - fZ); - return [L, a, b2]; - }, - _lab: (r, g, b) => { - const key = (r << 16) | (g << 8) | b; - let v = Utils._labCache.get(key); - if (!v) { - v = Utils._rgbToLab(r, g, b); - Utils._labCache.set(key, v); - } - return v; - }, - findClosestPaletteColor: (r, g, b, palette) => { - // Use provided palette or derive from COLOR_MAP - if (!palette || palette.length === 0) { - palette = Object.values(CONFIG.COLOR_MAP) - .filter((c) => c.rgb) - .map((c) => [c.rgb.r, c.rgb.g, c.rgb.b]); - } - if (state.colorMatchingAlgorithm === 'legacy') { - let menorDist = Infinity; - let cor = [0, 0, 0]; - for (let i = 0; i < palette.length; i++) { - const [pr, pg, pb] = palette[i]; - const rmean = (pr + r) / 2; - const rdiff = pr - r; - const gdiff = pg - g; - const bdiff = pb - b; - const dist = Math.sqrt( - (((512 + rmean) * rdiff * rdiff) >> 8) + - 4 * gdiff * gdiff + - (((767 - rmean) * bdiff * bdiff) >> 8) - ); - if (dist < menorDist) { - menorDist = dist; - cor = [pr, pg, pb]; - } - } - return cor; - } - // LAB algorithm - const [Lt, at, bt] = Utils._lab(r, g, b); - const targetChroma = Math.sqrt(at * at + bt * bt); - let best = null; - let bestDist = Infinity; - for (let i = 0; i < palette.length; i++) { - const [pr, pg, pb] = palette[i]; - const [Lp, ap, bp] = Utils._lab(pr, pg, pb); - const dL = Lt - Lp; - const da = at - ap; - const db = bt - bp; - let dist = dL * dL + da * da + db * db; - if (state.enableChromaPenalty && targetChroma > 20) { - const candChroma = Math.sqrt(ap * ap + bp * bp); - if (candChroma < targetChroma) { - const chromaDiff = targetChroma - candChroma; - dist += chromaDiff * chromaDiff * state.chromaPenaltyWeight; - } - } - if (dist < bestDist) { - bestDist = dist; - best = palette[i]; - if (bestDist === 0) break; - } - } - return best || [0, 0, 0]; - }, - - isWhitePixel: (r, g, b) => { - const wt = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD; - return r >= wt && g >= wt && b >= wt; - }, - - resolveColor(targetRgb, availableColors, exactMatch = false) { - if (!availableColors || availableColors.length === 0) { - return { - id: null, - rgb: targetRgb, - }; - } - - const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}|${state.colorMatchingAlgorithm}|${ - state.enableChromaPenalty ? 'c' : 'nc' - }|${state.chromaPenaltyWeight}|${exactMatch ? 'exact' : 'closest'}`; - - if (colorCache.has(cacheKey)) return colorCache.get(cacheKey); - - // Check for an exact color match in availableColors. - // If found, return the matched color with its ID. - // If not found, return the target color with null ID. - // Cache the result for future lookups. - if (exactMatch) { - const match = availableColors.find( - (c) => c.rgb[0] === targetRgb[0] && c.rgb[1] === targetRgb[1] && c.rgb[2] === targetRgb[2] - ); - const result = match ? { id: match.id, rgb: [...match.rgb] } : { id: null, rgb: targetRgb }; - colorCache.set(cacheKey, result); - return result; - } - - // check for white using threshold - const whiteThreshold = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD; - if ( - targetRgb[0] >= whiteThreshold && - targetRgb[1] >= whiteThreshold && - targetRgb[2] >= whiteThreshold - ) { - const whiteEntry = availableColors.find( - (c) => - c.rgb[0] >= whiteThreshold && c.rgb[1] >= whiteThreshold && c.rgb[2] >= whiteThreshold - ); - if (whiteEntry) { - const result = { id: whiteEntry.id, rgb: [...whiteEntry.rgb] }; - colorCache.set(cacheKey, result); - return result; - } - } - - // find nearest color - let bestId = availableColors[0].id; - let bestRgb = [...availableColors[0].rgb]; - let bestScore = Infinity; - - if (state.colorMatchingAlgorithm === 'legacy') { - for (let i = 0; i < availableColors.length; i++) { - const c = availableColors[i]; - const [r, g, b] = c.rgb; - const rmean = (r + targetRgb[0]) / 2; - const rdiff = r - targetRgb[0]; - const gdiff = g - targetRgb[1]; - const bdiff = b - targetRgb[2]; - const dist = Math.sqrt( - (((512 + rmean) * rdiff * rdiff) >> 8) + - 4 * gdiff * gdiff + - (((767 - rmean) * bdiff * bdiff) >> 8) - ); - if (dist < bestScore) { - bestScore = dist; - bestId = c.id; - bestRgb = [...c.rgb]; - if (dist === 0) break; - } - } - } else { - const [Lt, at, bt] = Utils._lab(targetRgb[0], targetRgb[1], targetRgb[2]); - const targetChroma = Math.sqrt(at * at + bt * bt); - const penaltyWeight = state.enableChromaPenalty ? state.chromaPenaltyWeight || 0.15 : 0; - - for (let i = 0; i < availableColors.length; i++) { - const c = availableColors[i]; - const [r, g, b] = c.rgb; - const [L2, a2, b2] = Utils._lab(r, g, b); - const dL = Lt - L2, - da = at - a2, - db = bt - b2; - let dist = dL * dL + da * da + db * db; - - if (penaltyWeight > 0 && targetChroma > 20) { - const candChroma = Math.sqrt(a2 * a2 + b2 * b2); - if (candChroma < targetChroma) { - const cd = targetChroma - candChroma; - dist += cd * cd * penaltyWeight; - } - } - - if (dist < bestScore) { - bestScore = dist; - bestId = c.id; - bestRgb = [...c.rgb]; - if (dist === 0) break; - } - } - } - - const result = { id: bestId, rgb: bestRgb }; - colorCache.set(cacheKey, result); - - // limit the size of the cache - if (colorCache.size > 15000) { - const firstKey = colorCache.keys().next().value; - colorCache.delete(firstKey); - } - - return result; - }, - - createImageUploader: () => - new Promise((resolve) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = 'image/png,image/jpeg'; - input.onchange = () => { - const fr = new FileReader(); - fr.onload = () => resolve(fr.result); - fr.readAsDataURL(input.files[0]); - }; - input.click(); - }), - - createFileDownloader: (data, filename) => { - const blob = new Blob([data], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - }, - - createFileUploader: () => - new Promise((resolve, reject) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.json'; - input.onchange = (e) => { - const file = e.target.files[0]; - if (file) { - const reader = new FileReader(); - reader.onload = () => { - try { - const data = JSON.parse(reader.result); - resolve(data); - } catch (error) { - reject(new Error('Invalid JSON file')); - } - }; - reader.onerror = () => reject(new Error('File reading error')); - reader.readAsText(file); - } else { - reject(new Error('No file selected')); - } - }; - input.click(); - }), - - extractAvailableColors: () => { - const colorElements = document.querySelectorAll('.tooltip button[id^="color-"]'); - if (colorElements.length === 0) { - console.log('❌ No color elements found on page'); - return null; - } - // Separate available and unavailable colors - const availableColors = []; - const unavailableColors = []; - - Array.from(colorElements).forEach((el) => { - const id = Number.parseInt(el.id.replace('color-', '')); - if (id === 0) return; // Skip transparent color - - const rgbStr = el.style.backgroundColor.match(/\d+/g); - if (!rgbStr || rgbStr.length < 3) { - console.warn(`Skipping color element ${el.id} — cannot parse RGB`); - return; - } - const rgb = rgbStr.map(Number); - - // Find color name from COLOR_MAP - const colorInfo = Object.values(CONFIG.COLOR_MAP).find((color) => color.id === id); - const name = colorInfo ? colorInfo.name : `Unknown Color ${id}`; - - const colorData = { id, name, rgb }; - - // Check if color is available (no SVG overlay means available) - if (!el.querySelector('svg')) { - availableColors.push(colorData); - } else { - unavailableColors.push(colorData); - } - }); - - // Console log detailed color information - console.log('=== CAPTURED COLORS STATUS ==='); - console.log(`Total available colors: ${availableColors.length}`); - console.log(`Total unavailable colors: ${unavailableColors.length}`); - console.log(`Total colors scanned: ${availableColors.length + unavailableColors.length}`); - - if (availableColors.length > 0) { - console.log('\n--- AVAILABLE COLORS ---'); - availableColors.forEach((color, index) => { - console.log( - `${ - index + 1 - }. ID: ${color.id}, Name: "${color.name}", RGB: (${color.rgb[0]}, ${color.rgb[1]}, ${color.rgb[2]})` - ); - }); - } - - if (unavailableColors.length > 0) { - console.log('\n--- UNAVAILABLE COLORS ---'); - unavailableColors.forEach((color, index) => { - console.log( - `${ - index + 1 - }. ID: ${color.id}, Name: "${color.name}", RGB: (${color.rgb[0]}, ${color.rgb[1]}, ${color.rgb[2]}) [LOCKED]` - ); - }); - } - - console.log('=== END COLOR STATUS ==='); - - return availableColors; - }, - - formatTime: (ms) => { - const seconds = Math.floor((ms / 1000) % 60); - const minutes = Math.floor((ms / (1000 * 60)) % 60); - const hours = Math.floor((ms / (1000 * 60 * 60)) % 24); - const days = Math.floor(ms / (1000 * 60 * 60 * 24)); - - let result = ''; - if (days > 0) result += `${days}d `; - if (hours > 0 || days > 0) result += `${hours}h `; - if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `; - result += `${seconds}s`; - - return result; - }, - - calculateEstimatedTime: (remainingPixels, charges, cooldown) => { - if (remainingPixels <= 0) return 0; - - const paintingSpeedDelay = state.paintingSpeed > 0 ? 1000 / state.paintingSpeed : 1000; - const timeFromSpeed = remainingPixels * paintingSpeedDelay; - - const cyclesNeeded = Math.ceil(remainingPixels / Math.max(charges, 1)); - const timeFromCharges = cyclesNeeded * cooldown; - - return timeFromSpeed + timeFromCharges; // combine instead of taking max - }, - - // --- Painted pixel tracking helpers --- - initializePaintedMap: (width, height) => { - if (!state.paintedMap || state.paintedMap.length !== height) { - state.paintedMap = Array(height) - .fill() - .map(() => Array(width).fill(false)); - console.log(`📋 Initialized painted map: ${width}x${height}`); - } - }, - - markPixelPainted: (x, y, regionX = 0, regionY = 0) => { - const actualX = x + regionX; - const actualY = y + regionY; - - if ( - state.paintedMap && - state.paintedMap[actualY] && - actualX >= 0 && - actualX < state.paintedMap[actualY].length - ) { - state.paintedMap[actualY][actualX] = true; - } - }, - - isPixelPainted: (x, y, regionX = 0, regionY = 0) => { - const actualX = x + regionX; - const actualY = y + regionY; - - if ( - state.paintedMap && - state.paintedMap[actualY] && - actualX >= 0 && - actualX < state.paintedMap[actualY].length - ) { - return state.paintedMap[actualY][actualX]; - } - return false; - }, - - // Smart save - only save if significant changes - shouldAutoSave: () => { - const now = Date.now(); - const pixelsSinceLastSave = state.paintedPixels - state._lastSavePixelCount; - const timeSinceLastSave = now - state._lastSaveTime; - - // Save conditions: - // 1. Every 25 pixels (reduced from 50 for more frequent saves) - // 2. At least 30 seconds since last save (prevent spam) - // 3. Not already saving - return !state._saveInProgress && pixelsSinceLastSave >= 25 && timeSinceLastSave >= 30000; - }, - - performSmartSave: () => { - if (!Utils.shouldAutoSave()) return false; - - state._saveInProgress = true; - const success = Utils.saveProgress(); - - if (success) { - state._lastSavePixelCount = state.paintedPixels; - state._lastSaveTime = Date.now(); - console.log(`💾 Auto-saved at ${state.paintedPixels} pixels`); - } - - state._saveInProgress = false; - return success; - }, - - // --- Data management helpers --- - - // Base64 compression helpers for efficient storage - packPaintedMapToBase64: (paintedMap, width, height) => { - if (!paintedMap || !width || !height) return null; - const totalBits = width * height; - const byteLen = Math.ceil(totalBits / 8); - const bytes = new Uint8Array(byteLen); - let bitIndex = 0; - for (let y = 0; y < height; y++) { - const row = paintedMap[y]; - for (let x = 0; x < width; x++) { - const bit = row && row[x] ? 1 : 0; - const b = bitIndex >> 3; // byte index - const o = bitIndex & 7; // bit offset - if (bit) bytes[b] |= 1 << o; - bitIndex++; - } - } - let binary = ''; - const chunk = 0x8000; - for (let i = 0; i < bytes.length; i += chunk) { - binary += String.fromCharCode.apply( - null, - bytes.subarray(i, Math.min(i + chunk, bytes.length)) - ); - } - return btoa(binary); - }, - - unpackPaintedMapFromBase64: (base64, width, height) => { - if (!base64 || !width || !height) return null; - const binary = atob(base64); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); - const map = Array(height) - .fill() - .map(() => Array(width).fill(false)); - let bitIndex = 0; - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x++) { - const b = bitIndex >> 3; - const o = bitIndex & 7; - map[y][x] = ((bytes[b] >> o) & 1) === 1; - bitIndex++; - } - } - return map; - }, - - // Migration helpers for backward compatibility - migrateProgressToV2: (saved) => { - if (!saved) return saved; - const isV1 = - !saved.version || - saved.version === '1' || - saved.version === '1.0' || - saved.version === '1.1'; - if (!isV1) return saved; - - try { - const migrated = { ...saved }; - const width = migrated.imageData?.width; - const height = migrated.imageData?.height; - if (migrated.paintedMap && width && height) { - const data = Utils.packPaintedMapToBase64(migrated.paintedMap, width, height); - migrated.paintedMapPacked = { width, height, data }; - } - delete migrated.paintedMap; - migrated.version = '2'; - return migrated; - } catch (e) { - console.warn('Migration to v2 failed, using original data:', e); - return saved; - } - }, - - migrateProgressToV21: (saved) => { - if (!saved) return saved; - if (saved.version === '2.1') return saved; - const isV2 = saved.version === '2' || saved.version === '2.0'; - const isV1 = - !saved.version || - saved.version === '1' || - saved.version === '1.0' || - saved.version === '1.1'; - if (!isV2 && !isV1) return saved; // save this for future - try { - const migrated = { ...saved }; - // First migrate to v2 if needed - if (isV1) { - const width = migrated.imageData?.width; - const height = migrated.imageData?.height; - if (migrated.paintedMap && width && height) { - const data = Utils.packPaintedMapToBase64(migrated.paintedMap, width, height); - migrated.paintedMapPacked = { width, height, data }; - } - delete migrated.paintedMap; - } - migrated.version = '2.1'; - return migrated; - } catch (e) { - console.warn('Migration to v2.1 failed, using original data:', e); - return saved; - } - }, - - migrateProgressToV22: (data) => { - try { - const migrated = { ...data }; - migrated.version = '2.2'; - - // Add new fields with default values - if (!migrated.state.coordinateMode) { - migrated.state.coordinateMode = CONFIG.COORDINATE_MODE; - } - if (!migrated.state.coordinateDirection) { - migrated.state.coordinateDirection = CONFIG.COORDINATE_DIRECTION; - } - if (!migrated.state.coordinateSnake) { - migrated.state.coordinateSnake = CONFIG.COORDINATE_SNAKE; - } - if (!migrated.state.blockWidth) { - migrated.state.blockWidth = CONFIG.COORDINATE_BLOCK_WIDTH; - } - if (!migrated.state.blockHeight) { - migrated.state.blockHeight = CONFIG.COORDINATE_BLOCK_HEIGHT; - } - - return migrated; - } catch (e) { - console.warn('Migration to v2.2 failed, using original data:', e); - return data; - } - }, - - buildPaintedMapPacked() { - if (state.paintedMap && state.imageData) { - const data = Utils.packPaintedMapToBase64( - state.paintedMap, - state.imageData.width, - state.imageData.height - ); - if (data) { - return { - width: state.imageData.width, - height: state.imageData.height, - data: data, - }; - } - } - return null; - }, - - buildProgressData() { - return { - timestamp: Date.now(), - version: '2.2', - state: { - totalPixels: state.totalPixels, - paintedPixels: state.paintedPixels, - lastPosition: state.lastPosition, - startPosition: state.startPosition, - region: state.region, - imageLoaded: state.imageLoaded, - colorsChecked: state.colorsChecked, - coordinateMode: state.coordinateMode, - coordinateDirection: state.coordinateDirection, - coordinateSnake: state.coordinateSnake, - blockWidth: state.blockWidth, - blockHeight: state.blockHeight, - availableColors: state.availableColors, - }, - imageData: state.imageData - ? { - width: state.imageData.width, - height: state.imageData.height, - pixels: Array.from(state.imageData.pixels), - totalPixels: state.imageData.totalPixels, - } - : null, - paintedMapPacked: Utils.buildPaintedMapPacked(), - }; - }, - - migrateProgress(saved) { - if (!saved) return null; - - let data = saved; - const ver = data.version; - - // If version is missing or ≤ 1.x → first migrate to v2 - if (!ver || ver === '1' || ver === '1.0' || ver === '1.1') { - data = Utils.migrateProgressToV2(data); - } - - // If still older than v2.1 → migrate to 2.1 - if (data.version === '2' || data.version === '2.0') { - data = Utils.migrateProgressToV21(data); - } - - // If still older than v2.2 → migrate to 2.2 - if (data.version === '2.1') { - data = Utils.migrateProgressToV22(data); - } - - // Now data is guaranteed to be the latest version - return data; - }, - - saveProgress: () => { - try { - const progressData = Utils.buildProgressData(state); - - localStorage.setItem('wplace-bot-progress', JSON.stringify(progressData)); - return true; - } catch (error) { - console.error('Error saving progress:', error); - return false; - } - }, - - loadProgress: () => { - try { - const saved = localStorage.getItem('wplace-bot-progress'); - if (!saved) return null; - let data = JSON.parse(saved); - const migrated = Utils.migrateProgress(data); - - if (migrated && migrated !== data) { - try { - localStorage.setItem('wplace-bot-progress', JSON.stringify(migrated)); - } catch {} - } - return migrated; - } catch (error) { - console.error('Error loading progress:', error); - return null; - } - }, - - clearProgress: () => { - try { - localStorage.removeItem('wplace-bot-progress'); - // Also clear painted map from memory - state.paintedMap = null; - state._lastSavePixelCount = 0; - state._lastSaveTime = 0; - // Reset coordinate generation settings to their default values - state.coordinateMode = CONFIG.COORDINATE_MODE; - state.coordinateDirection = CONFIG.COORDINATE_DIRECTION; - state.coordinateSnake = CONFIG.COORDINATE_SNAKE; - state.blockWidth = CONFIG.COORDINATE_BLOCK_WIDTH; - state.blockHeight = CONFIG.COORDINATE_BLOCK_HEIGHT; - console.log('📋 Progress and painted map cleared'); - return true; - } catch (error) { - console.error('Error clearing progress:', error); - return false; - } - }, - - restoreProgress: (savedData) => { - try { - Object.assign(state, savedData.state); - - // Restore coordinate generation settings - if (savedData.state.coordinateMode) { - state.coordinateMode = savedData.state.coordinateMode; - } - if (savedData.state.coordinateDirection) { - state.coordinateDirection = savedData.state.coordinateDirection; - } - if (savedData.state.coordinateSnake !== undefined) { - state.coordinateSnake = savedData.state.coordinateSnake; - } - if (savedData.state.blockWidth) { - state.blockWidth = savedData.state.blockWidth; - } - if (savedData.state.blockHeight) { - state.blockHeight = savedData.state.blockHeight; - } - - if (savedData.imageData) { - state.imageData = { - ...savedData.imageData, - pixels: new Uint8ClampedArray(savedData.imageData.pixels), - }; - - try { - const canvas = document.createElement('canvas'); - canvas.width = state.imageData.width; - canvas.height = state.imageData.height; - const ctx = canvas.getContext('2d'); - const imageData = new ImageData( - state.imageData.pixels, - state.imageData.width, - state.imageData.height - ); - ctx.putImageData(imageData, 0, 0); - const proc = new ImageProcessor(''); - proc.img = canvas; - proc.canvas = canvas; - proc.ctx = ctx; - state.imageData.processor = proc; - } catch (e) { - console.warn('Could not rebuild processor from saved image data:', e); - } - } - - // Prefer packed form if available; fallback to legacy paintedMap array for backward compatibility - if (savedData.paintedMapPacked && savedData.paintedMapPacked.data) { - const { width, height, data } = savedData.paintedMapPacked; - state.paintedMap = Utils.unpackPaintedMapFromBase64(data, width, height); - } else if (savedData.paintedMap) { - state.paintedMap = savedData.paintedMap.map((row) => Array.from(row)); - } - - return true; - } catch (error) { - console.error('Error restoring progress:', error); - return false; - } - }, - - saveProgressToFile: () => { - try { - const progressData = Utils.buildProgressData(); - const filename = `wplace-bot-progress-${new Date() - .toISOString() - .slice(0, 19) - .replace(/:/g, '-')}.json`; - Utils.createFileDownloader(JSON.stringify(progressData, null, 2), filename); - return true; - } catch (error) { - console.error('Error saving to file:', error); - return false; - } - }, - - loadProgressFromFile: async () => { - try { - const data = await Utils.createFileUploader(); - if (!data || !data.state) { - throw new Error('Invalid file format'); - } - const migrated = Utils.migrateProgress(data); - - const success = Utils.restoreProgress(migrated); - return success; - } catch (error) { - console.error('Error loading from file:', error); - throw error; - } - }, - - // Helper function to restore overlay from loaded data - restoreOverlayFromData: async () => { - if (!state.imageLoaded || !state.imageData || !state.startPosition || !state.region) { - return false; - } - - try { - // Recreate ImageBitmap from loaded pixel data - const imageData = new ImageData( - state.imageData.pixels, - state.imageData.width, - state.imageData.height - ); - - const canvas = new OffscreenCanvas(state.imageData.width, state.imageData.height); - const ctx = canvas.getContext('2d'); - ctx.putImageData(imageData, 0, 0); - const imageBitmap = await canvas.transferToImageBitmap(); - - // Set up overlay with restored data - await overlayManager.setImage(imageBitmap); - await overlayManager.setPosition(state.startPosition, state.region); - overlayManager.enable(); - - // Update overlay button state - const toggleOverlayBtn = document.getElementById('toggleOverlayBtn'); - if (toggleOverlayBtn) { - toggleOverlayBtn.disabled = false; - toggleOverlayBtn.classList.add('active'); - } - - console.log('Overlay restored from data'); - return true; - } catch (error) { - console.error('Failed to restore overlay from data:', error); - return false; - } - }, - - updateCoordinateUI({ mode, directionControls, snakeControls, blockControls }) { - const isLinear = mode === 'rows' || mode === 'columns'; - const isBlock = mode === 'blocks' || mode === 'shuffle-blocks'; - - if (directionControls) directionControls.style.display = isLinear ? 'block' : 'none'; - if (snakeControls) snakeControls.style.display = isLinear ? 'block' : 'none'; - if (blockControls) blockControls.style.display = isBlock ? 'block' : 'none'; - }, - }; - - // IMAGE PROCESSOR CLASS - class ImageProcessor { - constructor(imageSrc) { - this.imageSrc = imageSrc; - this.img = null; - this.canvas = null; - this.ctx = null; - } - - async load() { - return new Promise((resolve, reject) => { - this.img = new Image(); - this.img.crossOrigin = 'anonymous'; - this.img.onload = () => { - this.canvas = document.createElement('canvas'); - this.ctx = this.canvas.getContext('2d'); - this.canvas.width = this.img.width; - this.canvas.height = this.img.height; - this.ctx.drawImage(this.img, 0, 0); - resolve(); - }; - this.img.onerror = reject; - this.img.src = this.imageSrc; - }); - } - - getDimensions() { - return { - width: this.canvas.width, - height: this.canvas.height, - }; - } - - getPixelData() { - return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height).data; - } - - resize(newWidth, newHeight) { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); - - tempCanvas.width = newWidth; - tempCanvas.height = newHeight; - - tempCtx.imageSmoothingEnabled = false; - tempCtx.drawImage(this.canvas, 0, 0, newWidth, newHeight); - - this.canvas.width = newWidth; - this.canvas.height = newHeight; - this.ctx.imageSmoothingEnabled = false; - this.ctx.drawImage(tempCanvas, 0, 0); - - return this.ctx.getImageData(0, 0, newWidth, newHeight).data; - } - - generatePreview(width, height) { - const previewCanvas = document.createElement('canvas'); - const previewCtx = previewCanvas.getContext('2d'); - - previewCanvas.width = width; - previewCanvas.height = height; - - previewCtx.imageSmoothingEnabled = false; - previewCtx.drawImage(this.img, 0, 0, width, height); - - return previewCanvas.toDataURL(); - } - } - - // WPLACE API SERVICE - const WPlaceService = { - async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color) { - try { - await ensureToken(); - if (!turnstileToken) return 'token_error'; - const payload = { - coords: [pixelX, pixelY], - colors: [color], - t: turnstileToken, - }; - const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { - method: 'POST', - headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, - credentials: 'include', - body: JSON.stringify(payload), - }); - if (res.status === 403) { - console.error('❌ 403 Forbidden. Turnstile token might be invalid or expired.'); - turnstileToken = null; - tokenPromise = new Promise((resolve) => { - _resolveToken = resolve; - }); - return 'token_error'; - } - const data = await res.json(); - return data?.painted === 1; - } catch (e) { - console.error('Paint request failed:', e); - return false; - } - }, - - async getCharges() { - const defaultResult = { - charges: 0, - max: 1, - cooldown: CONFIG.COOLDOWN_DEFAULT, - }; - - try { - const res = await fetch('https://backend.wplace.live/me', { - credentials: 'include', - }); - - if (!res.ok) { - console.error(`Failed to get charges: HTTP ${res.status}`); - return defaultResult; - } - - const data = await res.json(); - - return { - charges: data.charges?.count ?? 0, - max: data.charges?.max ?? 1, - cooldown: data.charges?.cooldownMs ?? CONFIG.COOLDOWN_DEFAULT, - }; - } catch (e) { - console.error('Failed to get charges:', e); - return defaultResult; - } - }, - }; - - // Desktop Notification Manager - const NotificationManager = { - pollTimer: null, - pollIntervalMs: 60_000, - icon() { - const link = document.querySelector("link[rel~='icon']"); - return link?.href || location.origin + '/favicon.ico'; - }, - async requestPermission() { - if (!('Notification' in window)) { - Utils.showAlert(Utils.t('notificationsNotSupported'), 'warning'); - return 'denied'; - } - if (Notification.permission === 'granted') return 'granted'; - try { - const perm = await Notification.requestPermission(); - return perm; - } catch { - return Notification.permission; - } - }, - canNotify() { - return ( - state.notificationsEnabled && - typeof Notification !== 'undefined' && - Notification.permission === 'granted' - ); - }, - notify(title, body, tag = 'wplace-charges', force = false) { - if (!this.canNotify()) return false; - if (!force && state.notifyOnlyWhenUnfocused && document.hasFocus()) return false; - try { - new Notification(title, { - body, - tag, - renotify: true, - icon: this.icon(), - badge: this.icon(), - silent: false, - }); - return true; - } catch { - // Graceful fallback - Utils.showAlert(body, 'info'); - return false; - } - }, - resetEdgeTracking() { - state._lastChargesBelow = state.displayCharges < state.cooldownChargeThreshold; - state._lastChargesNotifyAt = 0; - }, - maybeNotifyChargesReached(force = false) { - if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; - const reached = state.displayCharges >= state.cooldownChargeThreshold; - const now = Date.now(); - const repeatMs = Math.max(1, Number(state.notificationIntervalMinutes || 5)) * 60_000; - if (reached) { - const shouldEdge = state._lastChargesBelow || force; - const shouldRepeat = now - (state._lastChargesNotifyAt || 0) >= repeatMs; - if (shouldEdge || shouldRepeat) { - const msg = Utils.t('chargesReadyMessage', { - current: state.displayCharges, - max: state.maxCharges, - threshold: state.cooldownChargeThreshold, - }); - this.notify(Utils.t('chargesReadyNotification'), msg, 'wplace-notify-charges'); - state._lastChargesNotifyAt = now; - } - state._lastChargesBelow = false; - } else { - state._lastChargesBelow = true; - } - }, - startPolling() { - this.stopPolling(); - if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; - // lightweight background polling - this.pollTimer = setInterval(async () => { - try { - const { charges, cooldown, max } = await WPlaceService.getCharges(); - state.displayCharges = Math.floor(charges); - state.cooldown = cooldown; - state.maxCharges = Math.max(1, Math.floor(max)); - this.maybeNotifyChargesReached(); - } catch { - /* ignore */ - } - }, this.pollIntervalMs); - }, - stopPolling() { - if (this.pollTimer) { - clearInterval(this.pollTimer); - this.pollTimer = null; - } - }, - syncFromState() { - this.resetEdgeTracking(); - if (state.notificationsEnabled && state.notifyOnChargesReached) this.startPolling(); - else this.stopPolling(); - }, - }; - - // COLOR MATCHING FUNCTION - Optimized with caching - const colorCache = new Map(); - - // UI UPDATE FUNCTIONS (declared early to avoid reference errors) - let updateUI = () => {}; - let updateStats = (isManualRefresh) => {}; - let updateDataButtons = () => {}; - - function updateActiveColorPalette() { - state.activeColorPalette = []; - const activeSwatches = document.querySelectorAll('.wplace-color-swatch.active'); - if (activeSwatches) { - activeSwatches.forEach((swatch) => { - const rgbStr = swatch.getAttribute('data-rgb'); - if (rgbStr) { - const rgb = rgbStr.split(',').map(Number); - state.activeColorPalette.push(rgb); - } - }); - } - if (document.querySelector('.resize-container')?.style.display === 'block') { - _updateResizePreview(); - } - } - - function toggleAllColors(select, showingUnavailable = false) { - const swatches = document.querySelectorAll('.wplace-color-swatch'); - if (swatches) { - swatches.forEach((swatch) => { - // Only toggle colors that are available or if we're showing unavailable colors - const isUnavailable = swatch.classList.contains('unavailable'); - if (!isUnavailable || showingUnavailable) { - // Don't try to select unavailable colors - if (!isUnavailable) { - swatch.classList.toggle('active', select); - } - } - }); - } - updateActiveColorPalette(); - } - - function unselectAllPaidColors() { - const swatches = document.querySelectorAll('.wplace-color-swatch'); - if (swatches) { - swatches.forEach((swatch) => { - const colorId = parseInt(swatch.getAttribute('data-color-id'), 10); - if (!isNaN(colorId) && colorId >= 32) { - swatch.classList.toggle('active', false); - } - }); - } - updateActiveColorPalette(); - } - - function initializeColorPalette(container) { - const colorsContainer = container.querySelector('#colors-container'); - const showAllToggle = container.querySelector('#showAllColorsToggle'); - if (!colorsContainer) return; - - // Use already captured colors from state (captured during upload) - // Don't re-fetch colors here, use what was captured when user clicked upload - if (!state.availableColors || state.availableColors.length === 0) { - // If no colors have been captured yet, show message - colorsContainer.innerHTML = `
${Utils.t( - 'uploadImageFirst' - )}
`; - return; - } - - function populateColors(showUnavailable = false) { - colorsContainer.innerHTML = ''; - let availableCount = 0; - let totalCount = 0; - - // Convert COLOR_MAP to array and filter out transparent - const allColors = Object.values(CONFIG.COLOR_MAP).filter((color) => color.rgb !== null); - - allColors.forEach((colorData) => { - const { id, name, rgb } = colorData; - const rgbKey = `${rgb.r},${rgb.g},${rgb.b}`; - totalCount++; - - // Check if this color is available in the captured colors - const isAvailable = state.availableColors.some( - (c) => c.rgb[0] === rgb.r && c.rgb[1] === rgb.g && c.rgb[2] === rgb.b - ); - - // If not showing all colors and this color is not available, skip it - if (!showUnavailable && !isAvailable) { - return; - } - - if (isAvailable) availableCount++; - - const colorItem = Utils.createElement('div', { - className: 'wplace-color-item', - }); - const swatch = Utils.createElement('button', { - className: `wplace-color-swatch ${!isAvailable ? 'unavailable' : ''}`, - title: `${name} (ID: ${id})${!isAvailable ? ' (Unavailable)' : ''}`, - 'data-rgb': rgbKey, - 'data-color-id': id, - }); - swatch.style.backgroundColor = `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`; - - // Make unavailable colors visually distinct - if (!isAvailable) { - swatch.style.opacity = '0.4'; - swatch.style.filter = 'grayscale(50%)'; - swatch.disabled = true; - } else { - // Select available colors by default - swatch.classList.add('active'); - } - - const nameLabel = Utils.createElement( - 'span', - { - className: 'wplace-color-item-name', - style: !isAvailable ? 'color: #888; font-style: italic;' : '', - }, - name + (!isAvailable ? ' (N/A)' : '') - ); - - // Only add click listener for available colors - if (isAvailable) { - swatch.addEventListener('click', () => { - swatch.classList.toggle('active'); - updateActiveColorPalette(); - }); - } - - colorItem.appendChild(swatch); - colorItem.appendChild(nameLabel); - colorsContainer.appendChild(colorItem); - }); - - updateActiveColorPalette(); - } - - // Initialize with only available colors - populateColors(false); - - // Add toggle functionality - if (showAllToggle) { - showAllToggle.addEventListener('change', (e) => { - populateColors(e.target.checked); - }); - } - - container - .querySelector('#selectAllBtn') - ?.addEventListener('click', () => toggleAllColors(true, showAllToggle?.checked)); - container - .querySelector('#unselectAllBtn') - ?.addEventListener('click', () => toggleAllColors(false, showAllToggle?.checked)); - container - .querySelector('#unselectPaidBtn') - ?.addEventListener('click', () => unselectAllPaidColors()); - } - - async function handleCaptcha() { - const startTime = performance.now(); - - // Check user's token source preference - if (state.tokenSource === 'manual') { - console.log('🎯 Manual token source selected - using pixel placement automation'); - return await handleCaptchaFallback(); - } - - // Generator mode (pure) or Hybrid mode - try generator first - try { - // Use optimized token generation with automatic sitekey detection - const { sitekey, token: preGeneratedToken } = await Utils.obtainSitekeyAndToken(); - - if (!sitekey) { - throw new Error('No valid sitekey found'); - } - - console.log('🔑 Generating Turnstile token for sitekey:', sitekey); - console.log( - '🧭 UA:', - navigator.userAgent.substring(0, 50) + '...', - 'Platform:', - navigator.platform - ); - - // Add additional checks before token generation - if (!window.turnstile) { - await Utils.loadTurnstile(); - } - - let token = null; - - // ✅ Reuse pre-generated token if available and valid - if ( - preGeneratedToken && - typeof preGeneratedToken === 'string' && - preGeneratedToken.length > 20 - ) { - console.log('♻️ Reusing pre-generated token from sitekey detection phase'); - token = preGeneratedToken; - } - // ✅ Or use globally cached token if still valid - else if (isTokenValid()) { - console.log('♻️ Using existing cached token (from previous operation)'); - token = turnstileToken; - } - // ✅ Otherwise generate a new one - else { - console.log('🔐 No valid pre-generated or cached token, creating new one...'); - token = await Utils.executeTurnstile(sitekey, 'paint'); - if (token) { - setTurnstileToken(token); - } - } - - // 📊 Debug log - console.log( - `🔍 Token received - Type: ${typeof token}, Value: ${ - token - ? typeof token === 'string' - ? token.length > 50 - ? token.substring(0, 50) + '...' - : token - : JSON.stringify(token) - : 'null/undefined' - }, Length: ${token?.length || 0}` - ); - - // ✅ Final validation - if (typeof token === 'string' && token.length > 20) { - const duration = Math.round(performance.now() - startTime); - console.log(`✅ Turnstile token generated successfully in ${duration}ms`); - return token; - } else { - throw new Error( - `Invalid or empty token received - Type: ${typeof token}, Value: ${JSON.stringify( - token - )}, Length: ${token?.length || 0}` - ); - } - } catch (error) { - const duration = Math.round(performance.now() - startTime); - console.error(`❌ Turnstile token generation failed after ${duration}ms:`, error); - - // Fallback to manual pixel placement for hybrid mode - if (state.tokenSource === 'hybrid') { - console.log( - '🔄 Hybrid mode: Generator failed, automatically switching to manual pixel placement...' - ); - const fbToken = await handleCaptchaFallback(); - return fbToken; - } else { - // Pure generator mode - don't fallback, just fail - throw error; - } - } - } - - // Keep original method as fallback - 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); - } - }); - } - - async function createUI() { - await detectLanguage(); - - const existingContainer = document.getElementById('wplace-image-bot-container'); - const existingStats = document.getElementById('wplace-stats-container'); - const existingSettings = document.getElementById('wplace-settings-container'); - const existingResizeContainer = document.querySelector('.resize-container'); - const existingResizeOverlay = document.querySelector('.resize-overlay'); - - if (existingContainer) existingContainer.remove(); - if (existingStats) existingStats.remove(); - if (existingSettings) existingSettings.remove(); - if (existingResizeContainer) existingResizeContainer.remove(); - if (existingResizeOverlay) existingResizeOverlay.remove(); - - loadThemePreference(); - await initializeTranslations(); - - const theme = getCurrentTheme(); - applyTheme(); // <- new: set CSS vars and theme class before building UI - - function appendLinkOnce(href, attributes = {}) { - // Check if a link with the same href already exists in the document head - const exists = Array.from(document.head.querySelectorAll('link')).some( - (link) => link.href === href - ); - if (exists) return; - - // Create a new link element - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = href; - - // Add any additional attributes (e.g., data-* attributes) - for (const [key, value] of Object.entries(attributes)) { - link.setAttribute(key, value); - } - - // Append the link element to the document head - document.head.appendChild(link); - } - - appendLinkOnce('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'); - - if (theme.fontFamily.includes('Press Start 2P')) { - appendLinkOnce('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); - } - - appendLinkOnce( - 'https://wplace-autobot.github.io/WPlace-AutoBOT/main/auto-image-styles.css', - { 'data-wplace-theme': 'true' } - ); - - const container = document.createElement('div'); - container.id = 'wplace-image-bot-container'; - container.innerHTML = ` -
-
- - ${Utils.t('title')} -
-
- - - - -
-
-
- -
-
- ${Utils.t('initMessage')} -
-
-
-
-
- - -
-
🖼️ Image Management
-
-
- - -
-
- -
-
-
- - -
-
🎮 Painting Control
-
-
- - -
-
- -
-
-
- - -
-
⏱️ ${Utils.t('cooldownSettings')}
-
- -
-
- -
-
- - - - ${Utils.t('charges')} -
-
-
-
- - -
-
💾 Data Management
-
-
- - -
-
- - -
-
-
-
- `; - - // Stats Window - Separate UI - const statsContainer = document.createElement('div'); - statsContainer.id = 'wplace-stats-container'; - statsContainer.style.display = 'none'; - statsContainer.innerHTML = ` -
-
- - ${Utils.t('paintingStats')} -
-
- - -
-
-
-
-
-
-
${Utils.t( - 'initMessage' - )}
-
-
-
-
- `; - - // Modern Settings Container with Theme Support - // Use the theme variable already declared at the top of createUI function - const settingsContainer = document.createElement('div'); - settingsContainer.id = 'wplace-settings-container'; - - // Apply theme-based styling - const themeBackground = theme.primary - ? `linear-gradient(135deg, ${theme.primary} 0%, ${theme.secondary || theme.primary} 100%)` - : `linear-gradient(135deg, #667eea 0%, #764ba2 100%)`; - - settingsContainer.className = 'wplace-settings-container-base'; - // Apply theme-specific background - settingsContainer.style.background = themeBackground; - settingsContainer.style.cssText += ` - min-width: 420px; - max-width: 480px; - z-index: 99999; - color: ${theme.text || 'white'}; - font-family: ${theme.fontFamily || "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif"}; - box-shadow: ${ - theme.boxShadow || '0 20px 40px rgba(0,0,0,0.3), 0 0 0 1px rgba(255,255,255,0.1)' - }; - backdrop-filter: ${theme.backdropFilter || 'blur(10px)'}; - overflow: hidden; - animation: settings-slide-in 0.4s ease-out; - ${ - theme.animations?.glow - ? ` - box-shadow: ${theme.boxShadow || '0 20px 40px rgba(0,0,0,0.3)'}, - 0 0 30px ${theme.highlight || theme.neon || '#00ffff'}; - ` - : '' - } - `; - - // noinspection CssInvalidFunction - settingsContainer.innerHTML = ` -
-
-

- - ${Utils.t('settings')} -

- -
-
- -
- - -
- -
- -

- Generator mode creates tokens automatically. Hybrid mode falls back to manual when generator fails. Manual mode only uses pixel placement. -

-
-
- - -
- - -
- - -
- -
- -
-
- Overlay Opacity -
${Math.round(state.overlayOpacity * 100)}%
-
- -
- - -
-
- - -
- - -
- - - - - - -
-
- - -
- - - -
- - -
- - -
-
- ${Utils.t('batchSize')} -
-
-
- -
-
-
- - - - pixels -
-
-
-
- ${CONFIG.PAINTING_SPEED.MIN} - ${CONFIG.PAINTING_SPEED.MAX} -
-
- - -
-
-
- - -
-
- - -
-
-

- 🎲 Random batch size between min and max values -

-
- - - -
- - -
- - - -
- - -
- - -
- - -
- - -
- -
- - -
-
-
- - -
-
- - -
-
-

- 🧱 Block dimensions for block-based generation modes -

-
-
- - -
- -
- - - -
- ${Utils.t('repeatEvery')} - - ${Utils.t('minutesPl')} -
-
- - -
-
-
- - -
- -
- -
-
- - -
- -
- -
-
-
- - - - - `; - - const resizeContainer = document.createElement('div'); - resizeContainer.className = 'resize-container'; - resizeContainer.innerHTML = ` -

${Utils.t('resizeImage')}

-
- - - - - -
- - - - - - - 100% -
- Drag to pan • Pinch to zoom • Double‑tap to zoom -
-
-
- -
-
-
- - -
-
-
-
-
-
-
- -
- - 1 -
-
-
- -
- - 1 -
-
-
-
- -
- - - -
-
- - - Shift = Row • Alt = Column -
-
- -
-
-  Color Palette -
-
-
- -
-
- - - -
-
-
-
- -
-
-  Advanced Color Matching -
-
- - -
-
- ${Utils.t('chromaWeight')} - ${state.chromaPenaltyWeight} -
- -
- -
- - -
- -
-
- -
- - - -
- `; - - const resizeOverlay = document.createElement('div'); - resizeOverlay.className = 'resize-overlay'; - - document.body.appendChild(container); - document.body.appendChild(resizeOverlay); - document.body.appendChild(resizeContainer); - document.body.appendChild(statsContainer); - document.body.appendChild(settingsContainer); - - // Show the main container after all elements are appended - container.style.display = 'block'; - - const uploadBtn = container.querySelector('#uploadBtn'); - const resizeBtn = container.querySelector('#resizeBtn'); - const selectPosBtn = container.querySelector('#selectPosBtn'); - const startBtn = container.querySelector('#startBtn'); - const stopBtn = container.querySelector('#stopBtn'); - const saveBtn = container.querySelector('#saveBtn'); - const loadBtn = container.querySelector('#loadBtn'); - const saveToFileBtn = container.querySelector('#saveToFileBtn'); - const loadFromFileBtn = container.querySelector('#loadFromFileBtn'); - - container.querySelectorAll('.wplace-section-title').forEach((title) => { - // Add a right-side arrow if it doesn't exist - if (!title.querySelector('i.arrow')) { - const arrow = document.createElement('i'); - arrow.className = 'fas fa-chevron-down arrow'; // FontAwesome down arrow - title.appendChild(arrow); - } - - // Click event to toggle collapse/expand of the section - title.addEventListener('click', () => { - const section = title.parentElement; - section.classList.toggle('collapsed'); - }); - }); - - // Disable load/upload buttons until initial setup is complete (startup only) - if (loadBtn) { - loadBtn.disabled = !state.initialSetupComplete; - loadBtn.title = state.initialSetupComplete - ? '' - : '🔄 Waiting for initial setup to complete...'; - } - if (loadFromFileBtn) { - loadFromFileBtn.disabled = !state.initialSetupComplete; - loadFromFileBtn.title = state.initialSetupComplete - ? '' - : '🔄 Waiting for initial setup to complete...'; - } - if (uploadBtn) { - uploadBtn.disabled = !state.initialSetupComplete; - uploadBtn.title = state.initialSetupComplete - ? '' - : '🔄 Waiting for initial setup to complete...'; - } - - const minimizeBtn = container.querySelector('#minimizeBtn'); - const compactBtn = container.querySelector('#compactBtn'); - const statsBtn = container.querySelector('#statsBtn'); - const toggleOverlayBtn = container.querySelector('#toggleOverlayBtn'); - const statusText = container.querySelector('#statusText'); - const progressBar = container.querySelector('#progressBar'); - const statsArea = statsContainer.querySelector('#statsArea'); - const content = container.querySelector('.wplace-content'); - const closeStatsBtn = statsContainer.querySelector('#closeStatsBtn'); - const refreshChargesBtn = statsContainer.querySelector('#refreshChargesBtn'); - const cooldownSlider = container.querySelector('#cooldownSlider'); - const cooldownInput = container.querySelector('#cooldownInput'); - const cooldownDecrease = container.querySelector('#cooldownDecrease'); - const cooldownIncrease = container.querySelector('#cooldownIncrease'); - const cooldownValue = container.querySelector('#cooldownValue'); - - if (!uploadBtn || !selectPosBtn || !startBtn || !stopBtn) { - console.error('Some UI elements not found:', { - uploadBtn: !!uploadBtn, - selectPosBtn: !!selectPosBtn, - startBtn: !!startBtn, - stopBtn: !!stopBtn, - }); - } - - if (!statsContainer || !statsArea || !closeStatsBtn) { - // Note: base CSS now aligns with this layout: main panel at left:20px (width 280), stats at left:330px. - } - - const header = container.querySelector('.wplace-header'); - - makeDraggable(container); - - function makeDraggable(element) { - let pos1 = 0, - pos2 = 0, - pos3 = 0, - pos4 = 0; - let isDragging = false; - const header = - element.querySelector('.wplace-header') || element.querySelector('.wplace-settings-header'); - - if (!header) { - console.warn('No draggable header found for element:', element); - return; - } - - header.onmousedown = dragMouseDown; - - function dragMouseDown(e) { - if (e.target.closest('.wplace-header-btn') || e.target.closest('button')) return; - - e.preventDefault(); - isDragging = true; - - const rect = element.getBoundingClientRect(); - - element.style.transform = 'none'; - element.style.top = rect.top + 'px'; - element.style.left = rect.left + 'px'; - - pos3 = e.clientX; - pos4 = e.clientY; - element.classList.add('wplace-dragging'); - document.onmouseup = closeDragElement; - document.onmousemove = elementDrag; - - document.body.style.userSelect = 'none'; - } - - function elementDrag(e) { - if (!isDragging) return; - - e.preventDefault(); - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - - let newTop = element.offsetTop - pos2; - let newLeft = element.offsetLeft - pos1; - - const rect = element.getBoundingClientRect(); - const maxTop = window.innerHeight - rect.height; - const maxLeft = window.innerWidth - rect.width; - - newTop = Math.max(0, Math.min(newTop, maxTop)); - newLeft = Math.max(0, Math.min(newLeft, maxLeft)); - - element.style.top = newTop + 'px'; - element.style.left = newLeft + 'px'; - } - - function closeDragElement() { - isDragging = false; - element.classList.remove('wplace-dragging'); - document.onmouseup = null; - document.onmousemove = null; - document.body.style.userSelect = ''; - } - } - - makeDraggable(statsContainer); - makeDraggable(container); - - if (statsBtn && closeStatsBtn) { - statsBtn.addEventListener('click', () => { - const isVisible = statsContainer.style.display !== 'none'; - if (isVisible) { - statsContainer.style.display = 'none'; - statsBtn.innerHTML = ''; - statsBtn.title = Utils.t('showStats'); - } else { - statsContainer.style.display = 'block'; - statsBtn.innerHTML = ''; - statsBtn.title = Utils.t('hideStats'); - } - }); - - closeStatsBtn.addEventListener('click', () => { - statsContainer.style.display = 'none'; - statsBtn.innerHTML = ''; - statsBtn.title = Utils.t('showStats'); - }); - - if (refreshChargesBtn) { - refreshChargesBtn.addEventListener('click', async () => { - refreshChargesBtn.innerHTML = ''; - refreshChargesBtn.disabled = true; - - try { - await updateStats(true); - } catch (error) { - console.error('Error refreshing charges:', error); - } finally { - refreshChargesBtn.innerHTML = ''; - refreshChargesBtn.disabled = false; - } - }); - } - } - if (statsContainer && statsBtn) { - // Stats container starts hidden - user clicks button to show - statsBtn.innerHTML = ''; - statsBtn.title = Utils.t('showStats'); - } - - const settingsBtn = container.querySelector('#settingsBtn'); - const closeSettingsBtn = settingsContainer.querySelector('#closeSettingsBtn'); - const applySettingsBtn = settingsContainer.querySelector('#applySettingsBtn'); - - if (settingsBtn && closeSettingsBtn && applySettingsBtn) { - settingsBtn.addEventListener('click', () => { - const isVisible = settingsContainer.classList.contains('show'); - if (isVisible) { - settingsContainer.style.animation = 'settings-fade-out 0.3s ease-out forwards'; - settingsContainer.classList.remove('show'); - setTimeout(() => { - settingsContainer.style.animation = ''; - }, 300); - } else { - settingsContainer.style.top = '50%'; - settingsContainer.style.left = '50%'; - settingsContainer.style.transform = 'translate(-50%, -50%)'; - settingsContainer.classList.add('show'); - settingsContainer.style.animation = 'settings-slide-in 0.4s ease-out'; - } - }); - - closeSettingsBtn.addEventListener('click', () => { - settingsContainer.style.animation = 'settings-fade-out 0.3s ease-out forwards'; - settingsContainer.classList.remove('show'); - setTimeout(() => { - settingsContainer.style.animation = ''; - settingsContainer.style.top = '50%'; - settingsContainer.style.left = '50%'; - settingsContainer.style.transform = 'translate(-50%, -50%)'; - }, 300); - }); - - applySettingsBtn.addEventListener('click', () => { - // Sync advanced settings before save - const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); - if (colorAlgorithmSelect) state.colorMatchingAlgorithm = colorAlgorithmSelect.value; - const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); - if (enableChromaPenaltyToggle) - state.enableChromaPenalty = enableChromaPenaltyToggle.checked; - const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); - if (chromaPenaltyWeightSlider) - state.chromaPenaltyWeight = parseFloat(chromaPenaltyWeightSlider.value) || 0.15; - const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); - if (transparencyThresholdInput) { - const v = parseInt(transparencyThresholdInput.value, 10); - if (!isNaN(v) && v >= 0 && v <= 255) state.customTransparencyThreshold = v; - } - const whiteThresholdInput = document.getElementById('whiteThresholdInput'); - if (whiteThresholdInput) { - const v = parseInt(whiteThresholdInput.value, 10); - if (!isNaN(v) && v >= 200 && v <= 255) state.customWhiteThreshold = v; - } - // Update functional thresholds - CONFIG.TRANSPARENCY_THRESHOLD = state.customTransparencyThreshold; - CONFIG.WHITE_THRESHOLD = state.customWhiteThreshold; - // Notifications - const notifEnabledToggle = document.getElementById('notifEnabledToggle'); - const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); - const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); - const notifIntervalInput = document.getElementById('notifIntervalInput'); - if (notifEnabledToggle) state.notificationsEnabled = !!notifEnabledToggle.checked; - if (notifOnChargesToggle) state.notifyOnChargesReached = !!notifOnChargesToggle.checked; - if (notifOnlyUnfocusedToggle) - state.notifyOnlyWhenUnfocused = !!notifOnlyUnfocusedToggle.checked; - if (notifIntervalInput) { - const v = parseInt(notifIntervalInput.value, 10); - if (!isNaN(v) && v >= 1 && v <= 60) state.notificationIntervalMinutes = v; - } - saveBotSettings(); - Utils.showAlert(Utils.t('settingsSaved'), 'success'); - closeSettingsBtn.click(); - NotificationManager.syncFromState(); - }); - - makeDraggable(settingsContainer); - - const tokenSourceSelect = settingsContainer.querySelector('#tokenSourceSelect'); - if (tokenSourceSelect) { - tokenSourceSelect.addEventListener('change', (e) => { - state.tokenSource = e.target.value; - saveBotSettings(); - console.log(`🔑 Token source changed to: ${state.tokenSource}`); - const sourceNames = { - generator: 'Automatic Generator', - hybrid: 'Generator + Auto Fallback', - manual: 'Manual Pixel Placement', - }; - Utils.showAlert( - Utils.t('tokenSourceSet', { source: sourceNames[state.tokenSource] }), - 'success' - ); - }); - } - - // Batch mode controls - const batchModeSelect = settingsContainer.querySelector('#batchModeSelect'); - const normalBatchControls = settingsContainer.querySelector('#normalBatchControls'); - const randomBatchControls = settingsContainer.querySelector('#randomBatchControls'); - const randomBatchMin = settingsContainer.querySelector('#randomBatchMin'); - const randomBatchMax = settingsContainer.querySelector('#randomBatchMax'); - - if (batchModeSelect) { - batchModeSelect.addEventListener('change', (e) => { - state.batchMode = e.target.value; - - // Switch between normal and random controls - if (normalBatchControls && randomBatchControls) { - if (e.target.value === 'random') { - normalBatchControls.style.display = 'none'; - randomBatchControls.style.display = 'block'; - } else { - normalBatchControls.style.display = 'block'; - randomBatchControls.style.display = 'none'; - } - } - - saveBotSettings(); - console.log(`📦 Batch mode changed to: ${state.batchMode}`); - Utils.showAlert( - Utils.t('batchModeSet', { - mode: - state.batchMode === 'random' ? Utils.t('randomRange') : Utils.t('normalFixedSize'), - }), - 'success' - ); - }); - } - - if (randomBatchMin) { - randomBatchMin.addEventListener('input', (e) => { - const min = parseInt(e.target.value); - if (min >= 1 && min <= 1000) { - state.randomBatchMin = min; - // Ensure min doesn't exceed max - if (randomBatchMax && min > state.randomBatchMax) { - state.randomBatchMax = min; - randomBatchMax.value = min; - } - saveBotSettings(); - } - }); - } - - if (randomBatchMax) { - randomBatchMax.addEventListener('input', (e) => { - const max = parseInt(e.target.value); - if (max >= 1 && max <= 1000) { - state.randomBatchMax = max; - // Ensure max doesn't go below min - if (randomBatchMin && max < state.randomBatchMin) { - state.randomBatchMin = max; - randomBatchMin.value = max; - } - saveBotSettings(); - } - }); - } - - const languageSelect = settingsContainer.querySelector('#languageSelect'); - if (languageSelect) { - languageSelect.addEventListener('change', async (e) => { - const newLanguage = e.target.value; - state.language = newLanguage; - localStorage.setItem('wplace_language', newLanguage); - - // Load the new language translations - await loadTranslations(newLanguage); - - setTimeout(() => { - settingsContainer.style.display = 'none'; - createUI(); - }, 100); - }); - } - - const themeSelect = settingsContainer.querySelector('#themeSelect'); - if (themeSelect) { - themeSelect.addEventListener('change', (e) => { - const newTheme = e.target.value; - switchTheme(newTheme); - }); - } - - const overlayOpacitySlider = settingsContainer.querySelector('#overlayOpacitySlider'); - const overlayOpacityValue = settingsContainer.querySelector('#overlayOpacityValue'); - const enableBlueMarbleToggle = settingsContainer.querySelector('#enableBlueMarbleToggle'); - const settingsPaintWhiteToggle = settingsContainer.querySelector('#settingsPaintWhiteToggle'); - const settingsPaintTransparentToggle = settingsContainer.querySelector( - '#settingsPaintTransparentToggle' - ); - - if (overlayOpacitySlider && overlayOpacityValue) { - const updateOpacity = (newValue) => { - const opacity = parseFloat(newValue); - state.overlayOpacity = opacity; - overlayOpacitySlider.value = opacity; - overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`; - }; - - overlayOpacitySlider.addEventListener('input', (e) => { - updateOpacity(e.target.value); - }); - - // Add scroll-to-adjust for overlay opacity slider - Utils.createScrollToAdjust(overlayOpacitySlider, updateOpacity, 0, 1, 0.05); - } - - if (settingsPaintWhiteToggle) { - settingsPaintWhiteToggle.checked = state.paintWhitePixels; - settingsPaintWhiteToggle.addEventListener('change', (e) => { - state.paintWhitePixels = e.target.checked; - saveBotSettings(); - console.log(`🎨 Paint white pixels: ${state.paintWhitePixels ? 'ON' : 'OFF'}`); - const statusText = state.paintWhitePixels - ? 'White pixels in the template will be painted' - : 'White pixels will be skipped'; - Utils.showAlert(statusText, 'success'); - }); - } - - if (settingsPaintTransparentToggle) { - settingsPaintTransparentToggle.checked = state.paintTransparentPixels; - settingsPaintTransparentToggle.addEventListener('change', (e) => { - state.paintTransparentPixels = e.target.checked; - saveBotSettings(); - console.log( - `🎨 Paint transparent pixels: ${state.paintTransparentPixels ? 'ON' : 'OFF'}` - ); - const statusText = state.paintTransparentPixels - ? 'Transparent pixels in the template will be painted with the closest available color' - : 'Transparent pixels will be skipped'; - Utils.showAlert(statusText, 'success'); - }); - } - - // Speed controls - both slider and input - const speedSlider = settingsContainer.querySelector('#speedSlider'); - const speedInput = settingsContainer.querySelector('#speedInput'); - const speedDecrease = settingsContainer.querySelector('#speedDecrease'); - const speedIncrease = settingsContainer.querySelector('#speedIncrease'); - const speedValue = settingsContainer.querySelector('#speedValue'); - - if (speedSlider && speedInput && speedValue && speedDecrease && speedIncrease) { - const updateSpeed = (newValue) => { - const speed = Math.max(CONFIG.PAINTING_SPEED.MIN, Math.min(CONFIG.PAINTING_SPEED.MAX, parseInt(newValue))); - state.paintingSpeed = speed; - - // Update both controls (value shows in input, label shows unit only) - speedSlider.value = speed; - speedInput.value = speed; - speedValue.textContent = `pixels`; - - saveBotSettings(); - }; - - // Slider event listener - speedSlider.addEventListener('input', (e) => { - updateSpeed(e.target.value); - }); - - // Number input event listener - speedInput.addEventListener('input', (e) => { - updateSpeed(e.target.value); - }); - - // Decrease button - speedDecrease.addEventListener('click', () => { - updateSpeed(parseInt(speedInput.value) - 1); - }); - - // Increase button - speedIncrease.addEventListener('click', () => { - updateSpeed(parseInt(speedInput.value) + 1); - }); - - // Add scroll-to-adjust for speed slider - Utils.createScrollToAdjust(speedSlider, updateSpeed, CONFIG.PAINTING_SPEED.MIN, CONFIG.PAINTING_SPEED.MAX, 1); - } - - if (enableBlueMarbleToggle) { - enableBlueMarbleToggle.addEventListener('click', async () => { - state.blueMarbleEnabled = enableBlueMarbleToggle.checked; - if (state.imageLoaded && overlayManager.imageBitmap) { - Utils.showAlert(Utils.t('reprocessingOverlay'), 'info'); - await overlayManager.processImageIntoChunks(); - Utils.showAlert(Utils.t('overlayUpdated'), 'success'); - } - }); - } - - // (Advanced color listeners moved outside to work with resize dialog) - // (Advanced color listeners moved outside to work with resize dialog) - // Notifications listeners - const notifPermBtn = settingsContainer.querySelector('#notifRequestPermBtn'); - const notifTestBtn = settingsContainer.querySelector('#notifTestBtn'); - if (notifPermBtn) { - notifPermBtn.addEventListener('click', async () => { - const perm = await NotificationManager.requestPermission(); - if (perm === 'granted') Utils.showAlert(Utils.t('notificationsEnabled'), 'success'); - else Utils.showAlert(Utils.t('notificationsPermissionDenied'), 'warning'); - }); - } - if (notifTestBtn) { - notifTestBtn.addEventListener('click', () => { - NotificationManager.notify( - Utils.t('testNotificationTitle'), - Utils.t('testNotificationMessage'), - 'wplace-notify-test', - true - ); - }); - } - } - - const widthSlider = resizeContainer.querySelector('#widthSlider'); - const heightSlider = resizeContainer.querySelector('#heightSlider'); - const widthValue = resizeContainer.querySelector('#widthValue'); - const heightValue = resizeContainer.querySelector('#heightValue'); - const keepAspect = resizeContainer.querySelector('#keepAspect'); - const paintWhiteToggle = resizeContainer.querySelector('#paintWhiteToggle'); - const paintTransparentToggle = resizeContainer.querySelector('#paintTransparentToggle'); - const zoomSlider = resizeContainer.querySelector('#zoomSlider'); - const zoomValue = resizeContainer.querySelector('#zoomValue'); - const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); - const zoomOutBtn = resizeContainer.querySelector('#zoomOutBtn'); - const zoomFitBtn = resizeContainer.querySelector('#zoomFitBtn'); - const zoomActualBtn = resizeContainer.querySelector('#zoomActualBtn'); - const panModeBtn = resizeContainer.querySelector('#panModeBtn'); - const panStage = resizeContainer.querySelector('#resizePanStage'); - const canvasStack = resizeContainer.querySelector('#resizeCanvasStack'); - const baseCanvas = resizeContainer.querySelector('#resizeCanvas'); - const maskCanvas = resizeContainer.querySelector('#maskCanvas'); - const baseCtx = baseCanvas.getContext('2d'); - const maskCtx = maskCanvas.getContext('2d'); - const confirmResize = resizeContainer.querySelector('#confirmResize'); - const cancelResize = resizeContainer.querySelector('#cancelResize'); - const downloadPreviewBtn = resizeContainer.querySelector('#downloadPreviewBtn'); - const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn'); - - // Coordinate generation controls with smart visibility - const coordinateModeSelect = settingsContainer.querySelector('#coordinateModeSelect'); - const coordinateDirectionSelect = settingsContainer.querySelector('#coordinateDirectionSelect'); - const coordinateSnakeToggle = settingsContainer.querySelector('#coordinateSnakeToggle'); - const directionControls = settingsContainer.querySelector('#directionControls'); - const snakeControls = settingsContainer.querySelector('#snakeControls'); - const blockControls = settingsContainer.querySelector('#blockControls'); - const blockWidthInput = settingsContainer.querySelector('#blockWidthInput'); - const blockHeightInput = settingsContainer.querySelector('#blockHeightInput'); - const paintUnavailablePixelsToggle = settingsContainer.querySelector( - '#paintUnavailablePixelsToggle' - ); - - if (paintUnavailablePixelsToggle) { - paintUnavailablePixelsToggle.checked = state.paintUnavailablePixels; - paintUnavailablePixelsToggle.addEventListener('change', (e) => { - state.paintUnavailablePixels = e.target.checked; - saveBotSettings(); - console.log(`🎨 Paint unavailable colors: ${state.paintUnavailablePixels ? 'ON' : 'OFF'}`); - const statusText = state.paintUnavailablePixels - ? 'Unavailable template colors will be painted with the closest available color' - : 'Unavailable template colors will be skipped'; - Utils.showAlert(statusText, 'success'); - }); - } - if (coordinateModeSelect) { - coordinateModeSelect.value = state.coordinateMode; - coordinateModeSelect.addEventListener('change', (e) => { - state.coordinateMode = e.target.value; - Utils.updateCoordinateUI({ - mode: state.coordinateMode, - directionControls, - snakeControls, - blockControls, - }); - saveBotSettings(); - console.log(`🔄 Coordinate mode changed to: ${state.coordinateMode}`); - Utils.showAlert(`Coordinate mode set to: ${state.coordinateMode}`, 'success'); - }); - } - - if (coordinateDirectionSelect) { - coordinateDirectionSelect.value = state.coordinateDirection; - coordinateDirectionSelect.addEventListener('change', (e) => { - state.coordinateDirection = e.target.value; - saveBotSettings(); - console.log(`🧭 Coordinate direction changed to: ${state.coordinateDirection}`); - Utils.showAlert(`Coordinate direction set to: ${state.coordinateDirection}`, 'success'); - }); - } - - if (coordinateSnakeToggle) { - coordinateSnakeToggle.checked = state.coordinateSnake; - coordinateSnakeToggle.addEventListener('change', (e) => { - state.coordinateSnake = e.target.checked; - saveBotSettings(); - console.log(`🐍 Snake pattern ${state.coordinateSnake ? 'enabled' : 'disabled'}`); - Utils.showAlert( - `Snake pattern ${state.coordinateSnake ? 'enabled' : 'disabled'}`, - 'success' - ); - }); - } - - if (blockWidthInput) { - blockWidthInput.value = state.blockWidth; - blockWidthInput.addEventListener('input', (e) => { - const width = parseInt(e.target.value); - if (width >= 1 && width <= 50) { - state.blockWidth = width; - saveBotSettings(); - } - }); - } - - if (blockHeightInput) { - blockHeightInput.value = state.blockHeight; - blockHeightInput.addEventListener('change', (e) => { - const height = parseInt(e.target.value); - if (height >= 1 && height <= 50) { - state.blockHeight = height; - saveBotSettings(); - } - }); - } - - if (compactBtn) { - compactBtn.addEventListener('click', () => { - container.classList.toggle('wplace-compact'); - const isCompact = container.classList.contains('wplace-compact'); - - if (isCompact) { - compactBtn.innerHTML = ''; - compactBtn.title = Utils.t('expandMode'); - } else { - compactBtn.innerHTML = ''; - compactBtn.title = Utils.t('compactMode'); - } - }); - } - - if (minimizeBtn) { - minimizeBtn.addEventListener('click', () => { - state.minimized = !state.minimized; - if (state.minimized) { - container.classList.add('wplace-minimized'); - content.classList.add('wplace-hidden'); - minimizeBtn.innerHTML = ''; - minimizeBtn.title = Utils.t('restore'); - } else { - container.classList.remove('wplace-minimized'); - content.classList.remove('wplace-hidden'); - minimizeBtn.innerHTML = ''; - minimizeBtn.title = Utils.t('minimize'); - } - saveBotSettings(); - }); - } - - if (toggleOverlayBtn) { - toggleOverlayBtn.addEventListener('click', () => { - const isEnabled = overlayManager.toggle(); - toggleOverlayBtn.classList.toggle('active', isEnabled); - toggleOverlayBtn.setAttribute('aria-pressed', isEnabled ? 'true' : 'false'); - Utils.showAlert(isEnabled ? Utils.t('overlayEnabled') : Utils.t('overlayDisabled'), 'info'); - }); - } - - if (state.minimized) { - container.classList.add('wplace-minimized'); - content.classList.add('wplace-hidden'); - if (minimizeBtn) { - minimizeBtn.innerHTML = ''; - minimizeBtn.title = Utils.t('restore'); - } - } else { - container.classList.remove('wplace-minimized'); - content.classList.remove('wplace-hidden'); - if (minimizeBtn) { - minimizeBtn.innerHTML = ''; - minimizeBtn.title = Utils.t('minimize'); - } - } - - if (saveBtn) { - saveBtn.addEventListener('click', () => { - if (!state.imageLoaded) { - Utils.showAlert(Utils.t('missingRequirements'), 'error'); - return; - } - - const success = Utils.saveProgress(); - if (success) { - updateUI('autoSaved', 'success'); - Utils.showAlert(Utils.t('autoSaved'), 'success'); - } else { - Utils.showAlert(Utils.t('errorSavingProgress'), 'error'); - } - }); - } - - if (loadBtn) { - loadBtn.addEventListener('click', () => { - // Check if initial setup is complete - if (!state.initialSetupComplete) { - Utils.showAlert(Utils.t('pleaseWaitInitialSetup'), 'warning'); - return; - } - - const savedData = Utils.loadProgress(); - if (!savedData) { - updateUI('noSavedData', 'warning'); - Utils.showAlert(Utils.t('noSavedData'), 'warning'); - return; - } - - const confirmLoad = confirm( - `${Utils.t('savedDataFound')}\n\n` + - `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + - `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels` - ); - - if (confirmLoad) { - const success = Utils.restoreProgress(savedData); - if (success) { - updateUI('dataLoaded', 'success'); - Utils.showAlert(Utils.t('dataLoaded'), 'success'); - updateDataButtons(); - - updateStats(); - - // Restore overlay if image data was loaded from localStorage - Utils.restoreOverlayFromData().catch((error) => { - console.error('Failed to restore overlay from localStorage:', error); - }); - - if (!state.colorsChecked) { - uploadBtn.disabled = false; - } else { - uploadBtn.disabled = false; - selectPosBtn.disabled = false; - } - - if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { - startBtn.disabled = false; - } - } else { - Utils.showAlert(Utils.t('errorLoadingProgress'), 'error'); - } - } - }); - } - - if (saveToFileBtn) { - saveToFileBtn.addEventListener('click', () => { - const success = Utils.saveProgressToFile(); - if (success) { - updateUI('fileSaved', 'success'); - Utils.showAlert(Utils.t('fileSaved'), 'success'); - } else { - Utils.showAlert(Utils.t('fileError'), 'error'); - } - }); - } - - if (loadFromFileBtn) { - loadFromFileBtn.addEventListener('click', async () => { - // Check if initial setup is complete - if (!state.initialSetupComplete) { - Utils.showAlert(Utils.t('pleaseWaitFileSetup'), 'warning'); - return; - } - - try { - const success = await Utils.loadProgressFromFile(); - if (success) { - updateUI('fileLoaded', 'success'); - Utils.showAlert(Utils.t('fileLoaded'), 'success'); - updateDataButtons(); - - await updateStats(); - - // Restore overlay if image data was loaded from file - await Utils.restoreOverlayFromData().catch((error) => { - console.error('Failed to restore overlay from file:', error); - }); - - if (state.colorsChecked) { - uploadBtn.disabled = false; - selectPosBtn.disabled = false; - resizeBtn.disabled = false; - } else { - uploadBtn.disabled = false; - } - - if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { - startBtn.disabled = false; - } - } - } catch (error) { - if (error.message === 'Invalid JSON file') { - Utils.showAlert(Utils.t('invalidFileFormat'), 'error'); - } else { - Utils.showAlert(Utils.t('fileError'), 'error'); - } - } - }); - } - - updateUI = (messageKey, type = 'default', params = {}, silent = false) => { - const message = Utils.t(messageKey, params); - statusText.textContent = message; - statusText.className = `wplace-status status-${type}`; - - if (!silent) { - // Trigger animation only when silent = false - statusText.style.animation = 'none'; - void statusText.offsetWidth; // trick to restart the animation - statusText.style.animation = 'slide-in 0.3s ease-out'; - } - }; - - function updateChargeStatsDisplay(intervalMs) { - const currentChargesEl = document.getElementById('wplace-stat-charges-value'); - const fullChargeEl = document.getElementById('wplace-stat-fullcharge-value'); - if (!fullChargeEl && !currentChargesEl) return; - if (!state.fullChargeData) { - fullChargeEl.textContent = '--:--:--'; - return; - } - - const { current, max, cooldownMs, startTime, spentSinceShot } = state.fullChargeData; - const elapsed = Date.now() - startTime; - - // total charges including elapsed time and spent during painting since snapshot - const chargesGained = elapsed / cooldownMs; - const rawCharges = current + chargesGained - spentSinceShot; - const cappedCharges = Math.min(rawCharges, max); - - // rounding with 0.95 threshold - let displayCharges; - const fraction = cappedCharges - Math.floor(cappedCharges); - if (fraction >= 0.95) { - displayCharges = Math.ceil(cappedCharges); - } else { - displayCharges = Math.floor(cappedCharges); - } - - state.displayCharges = Math.max(0, displayCharges); - state.preciseCurrentCharges = cappedCharges; - - const remainingMs = getMsToTargetCharges(cappedCharges, max, state.cooldown, intervalMs); - const timeText = Utils.msToTimeText(remainingMs); - - if (currentChargesEl) { - currentChargesEl.innerHTML = `${state.displayCharges} / ${state.maxCharges}`; - } - - if ( - state.displayCharges < state.cooldownChargeThreshold && - !state.stopFlag && - state.running - ) { - updateChargesThresholdUI(intervalMs); - } - - if (fullChargeEl) { - if (state.displayCharges >= max) { - fullChargeEl.innerHTML = `FULL`; - } else { - fullChargeEl.innerHTML = ` - ${timeText} - `; - } - } - } - - updateStats = async (isManualRefresh = false) => { - const isForcedRefresh = isManualRefresh; - const isFirstCheck = !state.fullChargeData?.startTime; - - const minUpdateInterval = 60_000; - const maxUpdateInterval = 90_000; - const randomUpdateThreshold = - minUpdateInterval + Math.random() * (maxUpdateInterval - minUpdateInterval); - const timeSinceLastUpdate = Date.now() - (state.fullChargeData?.startTime || 0); - const isTimeToUpdate = timeSinceLastUpdate >= randomUpdateThreshold; - - const shouldCallApi = isForcedRefresh || isFirstCheck || isTimeToUpdate; - - if (shouldCallApi) { - const { charges, max, cooldown } = await WPlaceService.getCharges(); - state.displayCharges = Math.floor(charges); - state.preciseCurrentCharges = charges; - state.cooldown = cooldown; - state.maxCharges = Math.floor(max) > 1 ? Math.floor(max) : state.maxCharges; - - state.fullChargeData = { - current: charges, - max: max, - cooldownMs: cooldown, - startTime: Date.now(), - spentSinceShot: 0, - }; - // Evaluate notifications every time we refresh server-side charges - NotificationManager.maybeNotifyChargesReached(); - } - - if (state.fullChargeInterval) { - clearInterval(state.fullChargeInterval); - state.fullChargeInterval = null; - } - const intervalMs = 1000; - state.fullChargeInterval = setInterval( - () => updateChargeStatsDisplay(intervalMs), - intervalMs - ); - - if (cooldownSlider && cooldownSlider.max !== state.maxCharges) { - cooldownSlider.max = state.maxCharges; - } - if (cooldownInput && cooldownInput.max !== state.maxCharges) { - cooldownInput.max = state.maxCharges; - } - - let imageStatsHTML = ''; - if (state.imageLoaded) { - const progress = - state.totalPixels > 0 ? Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; - const remainingPixels = state.totalPixels - state.paintedPixels; - state.estimatedTime = Utils.calculateEstimatedTime( - remainingPixels, - state.displayCharges, - state.cooldown - ); - progressBar.style.width = `${progress}%`; - - imageStatsHTML = ` -
-
${Utils.t('progress')}
-
${progress}%
-
-
-
${Utils.t( - 'pixels' - )}
-
${state.paintedPixels}/${state.totalPixels}
-
-
-
${Utils.t( - 'estimatedTime' - )}
-
${Utils.formatTime(state.estimatedTime)}
-
- `; - } - - let colorSwatchesHTML = ''; - state.availableColors = state.availableColors.filter( - (c) => c.name !== 'Unknown CoIor NaN' && c.id !== null - ); - - const availableColors = Utils.extractAvailableColors(); - const newCount = Array.isArray(availableColors) ? availableColors.length : 0; - - if (newCount === 0 && isManualRefresh) { - Utils.showAlert(Utils.t('noColorsFound'), 'warning'); - } else if (newCount > 0 && state.availableColors.length < newCount) { - const oldCount = state.availableColors.length; - - Utils.showAlert( - Utils.t('colorsUpdated', { - oldCount, - newCount: newCount, - diffCount: newCount - oldCount, - }), - 'success' - ); - state.availableColors = availableColors; - } - if (state.colorsChecked) { - colorSwatchesHTML = state.availableColors - .map((color) => { - const rgbString = `rgb(${color.rgb.join(',')})`; - return `
`; - }) - .join(''); - } - - statsArea.innerHTML = ` - ${imageStatsHTML} -
-
- ${Utils.t('charges')} -
-
- ${state.displayCharges} / ${state.maxCharges} -
-
-
-
- ${Utils.t('fullChargeIn')} -
-
--:--:--
-
- ${ - state.colorsChecked - ? ` -
-
${Utils.t( - 'availableColors', - { count: state.availableColors.length } - )}
-
- ${colorSwatchesHTML} -
-
- ` - : '' - } - `; - - // should be after statsArea.innerHTML = '...'. todo make full stats ui update partial - updateChargeStatsDisplay(intervalMs); - }; - - updateDataButtons = () => { - const hasImageData = state.imageLoaded && state.imageData; - saveBtn.disabled = !hasImageData; - saveToFileBtn.disabled = !hasImageData; - }; - - updateDataButtons(); - - function showResizeDialog(processor) { - let baseProcessor = processor; - let width, height; - if (state.originalImage?.dataUrl) { - baseProcessor = new ImageProcessor(state.originalImage.dataUrl); - width = state.originalImage.width; - height = state.originalImage.height; - } else { - const dims = processor.getDimensions(); - width = dims.width; - height = dims.height; - } - const aspectRatio = width / height; - - const rs = state.resizeSettings; - widthSlider.max = width * 2; - heightSlider.max = height * 2; - let initialW = width; - let initialH = height; - if ( - rs && - Number.isFinite(rs.width) && - Number.isFinite(rs.height) && - rs.width > 0 && - rs.height > 0 - ) { - initialW = rs.width; - initialH = rs.height; - } - // Clamp to slider ranges - initialW = Math.max( - parseInt(widthSlider.min, 10) || 10, - Math.min(initialW, parseInt(widthSlider.max, 10)) - ); - initialH = Math.max( - parseInt(heightSlider.min, 10) || 10, - Math.min(initialH, parseInt(heightSlider.max, 10)) - ); - widthSlider.value = initialW; - heightSlider.value = initialH; - widthValue.textContent = initialW; - heightValue.textContent = initialH; - zoomSlider.value = 1; - if (zoomValue) zoomValue.textContent = '100%'; - paintWhiteToggle.checked = state.paintWhitePixels; - paintTransparentToggle.checked = state.paintTransparentPixels; - - let _previewTimer = null; - let _previewJobId = 0; - let _isDraggingSize = false; - let _zoomLevel = 1; - let _ditherWorkBuf = null; - let _ditherEligibleBuf = null; - const ensureDitherBuffers = (n) => { - if (!_ditherWorkBuf || _ditherWorkBuf.length !== n * 3) - _ditherWorkBuf = new Float32Array(n * 3); - if (!_ditherEligibleBuf || _ditherEligibleBuf.length !== n) - _ditherEligibleBuf = new Uint8Array(n); - return { work: _ditherWorkBuf, eligible: _ditherEligibleBuf }; - }; - let _maskImageData = null; - let _maskData = null; - let _dirty = null; - const _resetDirty = () => { - _dirty = { minX: Infinity, minY: Infinity, maxX: -1, maxY: -1 }; - }; - const _markDirty = (x, y) => { - if (!_dirty) _resetDirty(); - if (x < _dirty.minX) _dirty.minX = x; - if (y < _dirty.minY) _dirty.minY = y; - if (x > _dirty.maxX) _dirty.maxX = x; - if (y > _dirty.maxY) _dirty.maxY = y; - }; - const _flushDirty = () => { - if (!_dirty || _dirty.maxX < _dirty.minX || _dirty.maxY < _dirty.minY) return; - const x = Math.max(0, _dirty.minX); - const y = Math.max(0, _dirty.minY); - const w = Math.min(maskCanvas.width - x, _dirty.maxX - x + 1); - const h = Math.min(maskCanvas.height - y, _dirty.maxY - y + 1); - if (w > 0 && h > 0) maskCtx.putImageData(_maskImageData, 0, 0, x, y, w, h); - _resetDirty(); - }; - const _ensureMaskOverlayBuffers = (w, h, rebuildFromMask = false) => { - if (!_maskImageData || _maskImageData.width !== w || _maskImageData.height !== h) { - _maskImageData = maskCtx.createImageData(w, h); - _maskData = _maskImageData.data; - rebuildFromMask = true; - } - if (rebuildFromMask) { - const m = state.resizeIgnoreMask; - const md = _maskData; - md.fill(0); - if (m) { - for (let i = 0; i < m.length; i++) - if (m[i]) { - const p = i * 4; - md[p] = 255; - md[p + 1] = 0; - md[p + 2] = 0; - md[p + 3] = 150; - } - } - maskCtx.putImageData(_maskImageData, 0, 0); - _resetDirty(); - } - }; - const ensureMaskSize = (w, h) => { - if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { - state.resizeIgnoreMask = new Uint8Array(w * h); - } - baseCanvas.width = w; - baseCanvas.height = h; - maskCanvas.width = w; - maskCanvas.height = h; - maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); - // Ensure overlay buffers exist and rebuild from mask when dimensions change - _ensureMaskOverlayBuffers(w, h, true); - }; - _updateResizePreview = async () => { - const jobId = ++_previewJobId; - const newWidth = parseInt(widthSlider.value, 10); - const newHeight = parseInt(heightSlider.value, 10); - _zoomLevel = parseFloat(zoomSlider.value); - - widthValue.textContent = newWidth; - heightValue.textContent = newHeight; - - ensureMaskSize(newWidth, newHeight); - canvasStack.style.width = newWidth + 'px'; - canvasStack.style.height = newHeight + 'px'; - baseCtx.imageSmoothingEnabled = false; - if (!state.availableColors || state.availableColors.length === 0) { - if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { - await baseProcessor.load(); - } - baseCtx.clearRect(0, 0, newWidth, newHeight); - baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); - // Draw existing mask overlay buffer - maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); - if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); - updateZoomLayout(); - return; - } - if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { - await baseProcessor.load(); - } - baseCtx.clearRect(0, 0, newWidth, newHeight); - baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); - const imgData = baseCtx.getImageData(0, 0, newWidth, newHeight); - const data = imgData.data; - - const tThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - - const applyFSDither = () => { - const w = newWidth, - h = newHeight; - const n = w * h; - const { work, eligible } = ensureDitherBuffers(n); - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - const idx = y * w + x; - const i4 = idx * 4; - const r = data[i4], - g = data[i4 + 1], - b = data[i4 + 2], - a = data[i4 + 3]; - const isEligible = - (state.paintTransparentPixels || a >= tThresh) && - (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); - eligible[idx] = isEligible ? 1 : 0; - work[idx * 3] = r; - work[idx * 3 + 1] = g; - work[idx * 3 + 2] = b; - if (!isEligible) { - data[i4 + 3] = 0; // transparent in preview overlay - } - } - } - - const diffuse = (nx, ny, er, eg, eb, factor) => { - if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; - const nidx = ny * w + nx; - if (!eligible[nidx]) return; - const base = nidx * 3; - work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); - work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); - work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); - }; - - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - const idx = y * w + x; - if (!eligible[idx]) continue; - const base = idx * 3; - const r0 = work[base], - g0 = work[base + 1], - b0 = work[base + 2]; - const [nr, ng, nb] = Utils.findClosestPaletteColor( - r0, - g0, - b0, - state.activeColorPalette - ); - const i4 = idx * 4; - data[i4] = nr; - data[i4 + 1] = ng; - data[i4 + 2] = nb; - data[i4 + 3] = 255; - - const er = r0 - nr; - const eg = g0 - ng; - const eb = b0 - nb; - - diffuse(x + 1, y, er, eg, eb, 7 / 16); - diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); - diffuse(x, y + 1, er, eg, eb, 5 / 16); - diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); - } - } - }; - - // Skip expensive dithering while user is dragging sliders - if (state.ditheringEnabled && !_isDraggingSize) { - applyFSDither(); - } else { - for (let i = 0; i < data.length; i += 4) { - const r = data[i], - g = data[i + 1], - b = data[i + 2], - a = data[i + 3]; - if ( - (!state.paintTransparentPixels && a < tThresh) || - (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) - ) { - data[i + 3] = 0; - continue; - } - const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); - data[i] = nr; - data[i + 1] = ng; - data[i + 2] = nb; - data[i + 3] = 255; - } - } - - if (jobId !== _previewJobId) return; - baseCtx.putImageData(imgData, 0, 0); - maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); - if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); - updateZoomLayout(); - }; - - const onWidthInput = () => { - if (keepAspect.checked) { - heightSlider.value = Math.round(parseInt(widthSlider.value, 10) / aspectRatio); - } - _updateResizePreview(); - const curW = parseInt(widthSlider.value, 10); - const curH = parseInt(heightSlider.value, 10); - state.resizeSettings = { - baseWidth: width, - baseHeight: height, - width: curW, - height: curH, - }; - saveBotSettings(); - // Auto-fit after size changes - const fit = typeof computeFitZoom === 'function' ? computeFitZoom() : 1; - if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); - }; - - const onHeightInput = () => { - if (keepAspect.checked) { - widthSlider.value = Math.round(parseInt(heightSlider.value, 10) * aspectRatio); - } - _updateResizePreview(); - const curW = parseInt(widthSlider.value, 10); - const curH = parseInt(heightSlider.value, 10); - state.resizeSettings = { - baseWidth: width, - baseHeight: height, - width: curW, - height: curH, - }; - saveBotSettings(); - // Auto-fit after size changes - const fit = typeof computeFitZoom === 'function' ? computeFitZoom() : 1; - if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); - }; - - paintWhiteToggle.onchange = (e) => { - state.paintWhitePixels = e.target.checked; - _updateResizePreview(); - saveBotSettings(); - }; - - paintTransparentToggle.onchange = (e) => { - state.paintTransparentPixels = e.target.checked; - _updateResizePreview(); - saveBotSettings(); - }; - - let panX = 0, - panY = 0; - const clampPan = () => { - const wrapRect = panStage?.getBoundingClientRect() || { - width: 0, - height: 0, - }; - const w = (baseCanvas.width || 1) * _zoomLevel; - const h = (baseCanvas.height || 1) * _zoomLevel; - if (w <= wrapRect.width) { - panX = Math.floor((wrapRect.width - w) / 2); - } else { - const minX = wrapRect.width - w; - panX = Math.min(0, Math.max(minX, panX)); - } - if (h <= wrapRect.height) { - panY = Math.floor((wrapRect.height - h) / 2); - } else { - const minY = wrapRect.height - h; - panY = Math.min(0, Math.max(minY, panY)); - } - }; - let _panRaf = 0; - const applyPan = () => { - if (_panRaf) return; - _panRaf = requestAnimationFrame(() => { - clampPan(); - canvasStack.style.transform = `translate3d(${Math.round( - panX - )}px, ${Math.round(panY)}px, 0) scale(${_zoomLevel})`; - _panRaf = 0; - }); - }; - - const updateZoomLayout = () => { - const w = baseCanvas.width || 1, - h = baseCanvas.height || 1; - baseCanvas.style.width = w + 'px'; - baseCanvas.style.height = h + 'px'; - maskCanvas.style.width = w + 'px'; - maskCanvas.style.height = h + 'px'; - canvasStack.style.width = w + 'px'; - canvasStack.style.height = h + 'px'; - applyPan(); - }; - const applyZoom = (z) => { - _zoomLevel = Math.max(0.05, Math.min(20, z || 1)); - zoomSlider.value = _zoomLevel; - updateZoomLayout(); - if (zoomValue) zoomValue.textContent = `${Math.round(_zoomLevel * 100)}%`; - }; - zoomSlider.addEventListener('input', () => { - applyZoom(parseFloat(zoomSlider.value)); - }); - if (zoomInBtn) - zoomInBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) + 0.1)); - if (zoomOutBtn) - zoomOutBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) - 0.1)); - const computeFitZoom = () => { - const wrapRect = panStage?.getBoundingClientRect(); - if (!wrapRect) return 1; - const w = baseCanvas.width || 1; - const h = baseCanvas.height || 1; - const margin = 10; - const scaleX = (wrapRect.width - margin) / w; - const scaleY = (wrapRect.height - margin) / h; - return Math.max(0.05, Math.min(20, Math.min(scaleX, scaleY))); - }; - if (zoomFitBtn) - zoomFitBtn.addEventListener('click', () => { - applyZoom(computeFitZoom()); - centerInView(); - }); - if (zoomActualBtn) - zoomActualBtn.addEventListener('click', () => { - applyZoom(1); - centerInView(); - }); - - const centerInView = () => { - if (!panStage) return; - const rect = panStage.getBoundingClientRect(); - const w = (baseCanvas.width || 1) * _zoomLevel; - const h = (baseCanvas.height || 1) * _zoomLevel; - panX = Math.floor((rect.width - w) / 2); - panY = Math.floor((rect.height - h) / 2); - applyPan(); - }; - - let isPanning = false; - let startX = 0, - startY = 0, - startPanX = 0, - startPanY = 0; - let allowPan = false; // Space key - let panMode = false; // Explicit pan mode toggle for touch/one-button mice - const isPanMouseButton = (e) => e.button === 1 || e.button === 2; - const setCursor = (val) => { - if (panStage) panStage.style.cursor = val; - }; - const isPanActive = (e) => panMode || allowPan || isPanMouseButton(e); - const updatePanModeBtn = () => { - if (!panModeBtn) return; - panModeBtn.classList.toggle('active', panMode); - panModeBtn.setAttribute('aria-pressed', panMode ? 'true' : 'false'); - }; - if (panModeBtn) { - updatePanModeBtn(); - panModeBtn.addEventListener('click', () => { - panMode = !panMode; - updatePanModeBtn(); - setCursor(panMode ? 'grab' : ''); - }); - } - if (panStage) { - panStage.addEventListener('contextmenu', (e) => { - if (allowPan) e.preventDefault(); - }); - window.addEventListener('keydown', (e) => { - if (e.code === 'Space') { - allowPan = true; - setCursor('grab'); - } - }); - window.addEventListener('keyup', (e) => { - if (e.code === 'Space') { - allowPan = false; - if (!isPanning) setCursor(''); - } - }); - panStage.addEventListener('mousedown', (e) => { - if (!isPanActive(e)) return; - e.preventDefault(); - isPanning = true; - startX = e.clientX; - startY = e.clientY; - startPanX = panX; - startPanY = panY; - setCursor('grabbing'); - }); - window.addEventListener('mousemove', (e) => { - if (!isPanning) return; - const dx = e.clientX - startX; - const dy = e.clientY - startY; - panX = startPanX + dx; - panY = startPanY + dy; - applyPan(); - }); - window.addEventListener('mouseup', () => { - if (isPanning) { - isPanning = false; - setCursor(allowPan ? 'grab' : ''); - } - }); - panStage.addEventListener( - 'wheel', - (e) => { - if (!e.ctrlKey && !e.metaKey) return; - e.preventDefault(); - const rect = panStage.getBoundingClientRect(); - const cx = e.clientX - rect.left - panX; - const cy = e.clientY - rect.top - panY; - const before = _zoomLevel; - const step = Math.max(0.05, Math.min(0.5, Math.abs(e.deltaY) > 20 ? 0.2 : 0.1)); - const next = Math.max(0.05, Math.min(20, before + (e.deltaY > 0 ? -step : step))); - if (next === before) return; - const scale = next / before; - panX = panX - cx * (scale - 1); - panY = panY - cy * (scale - 1); - applyZoom(next); - }, - { passive: false } - ); - let lastTouchDist = null; - let touchStartTime = 0; - let doubleTapTimer = null; - panStage.addEventListener( - 'touchstart', - (e) => { - if (e.touches.length === 1) { - const t = e.touches[0]; - isPanning = true; - startX = t.clientX; - startY = t.clientY; - startPanX = panX; - startPanY = panY; - setCursor('grabbing'); - const now = Date.now(); - if (now - touchStartTime < 300) { - // double tap -> toggle 100%/fit - const z = Math.abs(_zoomLevel - 1) < 0.01 ? computeFitZoom() : 1; - applyZoom(z); - centerInView(); - if (doubleTapTimer) clearTimeout(doubleTapTimer); - } else { - touchStartTime = now; - doubleTapTimer = setTimeout(() => { - doubleTapTimer = null; - }, 320); - } - } else if (e.touches.length === 2) { - // Pinch start - const [a, b] = e.touches; - lastTouchDist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); - } - }, - { passive: true } - ); - panStage.addEventListener( - 'touchmove', - (e) => { - if (e.touches.length === 1 && isPanning) { - const t = e.touches[0]; - const dx = t.clientX - startX; - const dy = t.clientY - startY; - panX = startPanX + dx; - panY = startPanY + dy; - applyPan(); - } else if (e.touches.length === 2 && lastTouchDist != null) { - e.preventDefault(); - const [a, b] = e.touches; - const dist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); - const rect = panStage.getBoundingClientRect(); - const centerX = (a.clientX + b.clientX) / 2 - rect.left - panX; - const centerY = (a.clientY + b.clientY) / 2 - rect.top - panY; - const before = _zoomLevel; - const scale = dist / (lastTouchDist || dist); - const next = Math.max(0.05, Math.min(20, before * scale)); - if (next !== before) { - panX = panX - centerX * (next / before - 1); - panY = panY - centerY * (next / before - 1); - applyZoom(next); - } - lastTouchDist = dist; - } - }, - { passive: false } - ); - panStage.addEventListener('touchend', () => { - isPanning = false; - lastTouchDist = null; - setCursor(panMode || allowPan ? 'grab' : ''); - }); - } - const schedulePreview = () => { - if (_previewTimer) clearTimeout(_previewTimer); - const run = () => { - _previewTimer = null; - _updateResizePreview(); - }; - if (window.requestIdleCallback) { - _previewTimer = setTimeout(() => requestIdleCallback(run, { timeout: 150 }), 50); - } else { - _previewTimer = setTimeout(() => requestAnimationFrame(run), 50); - } - }; - // Track dragging to reduce work and skip dithering during drag - const markDragStart = () => { - _isDraggingSize = true; - }; - const markDragEnd = () => { - _isDraggingSize = false; - schedulePreview(); - }; - widthSlider.addEventListener('pointerdown', markDragStart); - heightSlider.addEventListener('pointerdown', markDragStart); - widthSlider.addEventListener('pointerup', markDragEnd); - heightSlider.addEventListener('pointerup', markDragEnd); - widthSlider.addEventListener('input', () => { - onWidthInput(); - schedulePreview(); - }); - heightSlider.addEventListener('input', () => { - onHeightInput(); - schedulePreview(); - }); - - // Mask painting UX: brush size, modes, row/column fills, and precise coords - let draggingMask = false; - let lastPaintX = -1, - lastPaintY = -1; - let brushSize = 1; - let rowColSize = 1; - let maskMode = 'ignore'; // 'ignore' | 'unignore' | 'toggle' - const brushEl = resizeContainer.querySelector('#maskBrushSize'); - const brushValEl = resizeContainer.querySelector('#maskBrushSizeValue'); - const btnIgnore = resizeContainer.querySelector('#maskModeIgnore'); - const btnUnignore = resizeContainer.querySelector('#maskModeUnignore'); - const btnToggle = resizeContainer.querySelector('#maskModeToggle'); - const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn'); - const invertMaskBtn = resizeContainer.querySelector('#invertMaskBtn'); - const rowColSizeEl = resizeContainer.querySelector('#rowColSize'); - const rowColSizeValEl = resizeContainer.querySelector('#rowColSizeValue'); - - const updateModeButtons = () => { - const map = [ - [btnIgnore, 'ignore'], - [btnUnignore, 'unignore'], - [btnToggle, 'toggle'], - ]; - for (const [el, m] of map) { - if (!el) continue; - const active = maskMode === m; - el.classList.toggle('active', active); - el.setAttribute('aria-pressed', active ? 'true' : 'false'); - } - }; - const setMode = (mode) => { - maskMode = mode; - updateModeButtons(); - }; - if (brushEl && brushValEl) { - brushEl.addEventListener('input', () => { - brushSize = parseInt(brushEl.value, 10) || 1; - brushValEl.textContent = brushSize; - }); - brushValEl.textContent = brushEl.value; - brushSize = parseInt(brushEl.value, 10) || 1; - } - if (rowColSizeEl && rowColSizeValEl) { - rowColSizeEl.addEventListener('input', () => { - rowColSize = parseInt(rowColSizeEl.value, 10) || 1; - rowColSizeValEl.textContent = rowColSize; - }); - rowColSizeValEl.textContent = rowColSizeEl.value; - rowColSize = parseInt(rowColSizeEl.value, 10) || 1; - } - if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore')); - if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore')); - if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle')); - // Initialize button state (default to toggle mode) - updateModeButtons(); - - const mapClientToPixel = (clientX, clientY) => { - // Compute without rounding until final step to avoid drift at higher zoom - const rect = baseCanvas.getBoundingClientRect(); - const scaleX = rect.width / baseCanvas.width; - const scaleY = rect.height / baseCanvas.height; - const dx = (clientX - rect.left) / scaleX; - const dy = (clientY - rect.top) / scaleY; - const x = Math.floor(dx); - const y = Math.floor(dy); - return { x, y }; - }; - - const ensureMask = (w, h) => { - if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { - state.resizeIgnoreMask = new Uint8Array(w * h); - } - }; - - const paintCircle = (cx, cy, radius, value) => { - const w = baseCanvas.width, - h = baseCanvas.height; - ensureMask(w, h); - const r2 = radius * radius; - for (let yy = cy - radius; yy <= cy + radius; yy++) { - if (yy < 0 || yy >= h) continue; - for (let xx = cx - radius; xx <= cx + radius; xx++) { - if (xx < 0 || xx >= w) continue; - const dx = xx - cx, - dy = yy - cy; - if (dx * dx + dy * dy <= r2) { - const idx = yy * w + xx; - let val = state.resizeIgnoreMask[idx]; - if (maskMode === 'toggle') { - val = val ? 0 : 1; - } else if (maskMode === 'ignore') { - val = 1; - } else { - val = 0; - } - state.resizeIgnoreMask[idx] = val; - if (_maskData) { - const p = idx * 4; - if (val) { - _maskData[p] = 255; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 150; - } else { - _maskData[p] = 0; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 0; - } - _markDirty(xx, yy); - } - } - } - } - }; - - const paintRow = (y, value) => { - const w = baseCanvas.width, - h = baseCanvas.height; - ensureMask(w, h); - if (y < 0 || y >= h) return; - - // Paint multiple rows based on rowColSize - const halfSize = Math.floor(rowColSize / 2); - const startY = Math.max(0, y - halfSize); - const endY = Math.min(h - 1, y + halfSize); - - for (let rowY = startY; rowY <= endY; rowY++) { - for (let x = 0; x < w; x++) { - const idx = rowY * w + x; - let val = state.resizeIgnoreMask[idx]; - if (maskMode === 'toggle') { - val = val ? 0 : 1; - } else if (maskMode === 'ignore') { - val = 1; - } else { - val = 0; - } - state.resizeIgnoreMask[idx] = val; - if (_maskData) { - const p = idx * 4; - if (val) { - _maskData[p] = 255; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 150; - } else { - _maskData[p] = 0; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 0; - } - } - } - if (_maskData) { - _markDirty(0, rowY); - _markDirty(w - 1, rowY); - } - } - }; - - const paintColumn = (x, value) => { - const w = baseCanvas.width, - h = baseCanvas.height; - ensureMask(w, h); - if (x < 0 || x >= w) return; - - // Paint multiple columns based on rowColSize - const halfSize = Math.floor(rowColSize / 2); - const startX = Math.max(0, x - halfSize); - const endX = Math.min(w - 1, x + halfSize); - - for (let colX = startX; colX <= endX; colX++) { - for (let y = 0; y < h; y++) { - const idx = y * w + colX; - let val = state.resizeIgnoreMask[idx]; - if (maskMode === 'toggle') { - val = val ? 0 : 1; - } else if (maskMode === 'ignore') { - val = 1; - } else { - val = 0; - } - state.resizeIgnoreMask[idx] = val; - if (_maskData) { - const p = idx * 4; - if (val) { - _maskData[p] = 255; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 150; - } else { - _maskData[p] = 0; - _maskData[p + 1] = 0; - _maskData[p + 2] = 0; - _maskData[p + 3] = 0; - } - } - } - if (_maskData) { - _markDirty(colX, 0); - _markDirty(colX, h - 1); - } - } - }; - - const redrawMaskOverlay = () => { - // Only flush the dirty region; full rebuild happens on size change - _flushDirty(); - }; - - const handlePaint = (e) => { - // Suppress painting while panning - if ((e.buttons & 4) === 4 || (e.buttons & 2) === 2 || allowPan) return; - const { x, y } = mapClientToPixel(e.clientX, e.clientY); - const w = baseCanvas.width, - h = baseCanvas.height; - if (x < 0 || y < 0 || x >= w || y >= h) return; - const radius = Math.max(1, Math.floor(brushSize / 2)); - if (e.shiftKey) { - paintRow(y); - } else if (e.altKey) { - paintColumn(x); - } else { - paintCircle(x, y, radius); - } - lastPaintX = x; - lastPaintY = y; - redrawMaskOverlay(); - }; - - maskCanvas.addEventListener('mousedown', (e) => { - if (e.button === 1 || e.button === 2 || allowPan) return; // let pan handler manage - draggingMask = true; - handlePaint(e); - }); - // Avoid hijacking touch gestures for panning/zooming - maskCanvas.addEventListener( - 'touchstart', - (e) => { - /* let panStage handle */ - }, - { passive: true } - ); - maskCanvas.addEventListener( - 'touchmove', - (e) => { - /* let panStage handle */ - }, - { passive: true } - ); - maskCanvas.addEventListener( - 'touchend', - (e) => { - /* let panStage handle */ - }, - { passive: true } - ); - window.addEventListener('mousemove', (e) => { - if (draggingMask) handlePaint(e); - }); - window.addEventListener('mouseup', () => { - if (draggingMask) { - draggingMask = false; - saveBotSettings(); - } - }); - - if (clearIgnoredBtnEl) - clearIgnoredBtnEl.addEventListener('click', () => { - const w = baseCanvas.width, - h = baseCanvas.height; - if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0); - _ensureMaskOverlayBuffers(w, h, true); - _updateResizePreview(); - saveBotSettings(); - }); - - if (invertMaskBtn) - invertMaskBtn.addEventListener('click', () => { - if (!state.resizeIgnoreMask) return; - for (let i = 0; i < state.resizeIgnoreMask.length; i++) - state.resizeIgnoreMask[i] = state.resizeIgnoreMask[i] ? 0 : 1; - const w = baseCanvas.width, - h = baseCanvas.height; - _ensureMaskOverlayBuffers(w, h, true); - _updateResizePreview(); - saveBotSettings(); - }); - - confirmResize.onclick = async () => { - const newWidth = parseInt(widthSlider.value, 10); - const newHeight = parseInt(heightSlider.value, 10); - - // Generate the final paletted image data - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); - tempCanvas.width = newWidth; - tempCanvas.height = newHeight; - tempCtx.imageSmoothingEnabled = false; - if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { - await baseProcessor.load(); - } - tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); - const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight); - const data = imgData.data; - const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - let totalValidPixels = 0; - const mask = - state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight - ? state.resizeIgnoreMask - : null; - - const applyFSDitherFinal = async () => { - const w = newWidth, - h = newHeight; - const n = w * h; - const { work, eligible } = ensureDitherBuffers(n); - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - const idx = y * w + x; - const i4 = idx * 4; - const r = data[i4], - g = data[i4 + 1], - b = data[i4 + 2], - a = data[i4 + 3]; - const masked = mask && mask[idx]; - const isEligible = - !masked && - (state.paintTransparentPixels || a >= tThresh2) && - (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); - eligible[idx] = isEligible ? 1 : 0; - work[idx * 3] = r; - work[idx * 3 + 1] = g; - work[idx * 3 + 2] = b; - if (!isEligible) { - data[i4 + 3] = 0; - } - } - // Yield to keep UI responsive - if ((y & 15) === 0) await Promise.resolve(); - } - - const diffuse = (nx, ny, er, eg, eb, factor) => { - if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; - const nidx = ny * w + nx; - if (!eligible[nidx]) return; - const base = nidx * 3; - work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); - work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); - work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); - }; - - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - const idx = y * w + x; - if (!eligible[idx]) continue; - const base = idx * 3; - const r0 = work[base], - g0 = work[base + 1], - b0 = work[base + 2]; - const [nr, ng, nb] = Utils.findClosestPaletteColor( - r0, - g0, - b0, - state.activeColorPalette - ); - const i4 = idx * 4; - data[i4] = nr; - data[i4 + 1] = ng; - data[i4 + 2] = nb; - data[i4 + 3] = 255; - totalValidPixels++; - - const er = r0 - nr; - const eg = g0 - ng; - const eb = b0 - nb; - - diffuse(x + 1, y, er, eg, eb, 7 / 16); - diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); - diffuse(x, y + 1, er, eg, eb, 5 / 16); - diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); - } - // Yield every row to reduce jank - await Promise.resolve(); - } - }; - - if (state.ditheringEnabled) { - await applyFSDitherFinal(); - } else { - for (let i = 0; i < data.length; i += 4) { - const r = data[i], - g = data[i + 1], - b = data[i + 2], - a = data[i + 3]; - const masked = mask && mask[i >> 2]; - const isTransparent = (!state.paintTransparentPixels && a < tThresh2) || masked; - const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(r, g, b); - if (isTransparent || isWhiteAndSkipped) { - data[i + 3] = 0; // overlay transparency - continue; - } - totalValidPixels++; - const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); - data[i] = nr; - data[i + 1] = ng; - data[i + 2] = nb; - data[i + 3] = 255; - } - } - tempCtx.putImageData(imgData, 0, 0); - - // Save the final pixel data for painting - // Persist the paletted (and possibly dithered) pixels so painting uses the same output seen in overlay - const palettedPixels = new Uint8ClampedArray(imgData.data); - state.imageData.pixels = palettedPixels; - state.imageData.width = newWidth; - state.imageData.height = newHeight; - state.imageData.totalPixels = totalValidPixels; - state.totalPixels = totalValidPixels; - state.paintedPixels = 0; - - state.resizeSettings = { - baseWidth: width, - baseHeight: height, - width: newWidth, - height: newHeight, - }; - saveBotSettings(); - - const finalImageBitmap = await createImageBitmap(tempCanvas); - await overlayManager.setImage(finalImageBitmap); - overlayManager.enable(); - toggleOverlayBtn.classList.add('active'); - toggleOverlayBtn.setAttribute('aria-pressed', 'true'); - - // Keep state.imageData.processor as the original-based source; painting uses paletted pixels already stored - - updateStats(); - updateUI('resizeSuccess', 'success', { - width: newWidth, - height: newHeight, - }); - closeResizeDialog(); - }; - - downloadPreviewBtn.onclick = () => { - try { - const w = baseCanvas.width, - h = baseCanvas.height; - const out = document.createElement('canvas'); - out.width = w; - out.height = h; - const octx = out.getContext('2d'); - octx.imageSmoothingEnabled = false; - octx.drawImage(baseCanvas, 0, 0); - octx.drawImage(maskCanvas, 0, 0); - const link = document.createElement('a'); - link.download = 'wplace-preview.png'; - link.href = out.toDataURL(); - link.click(); - } catch (e) { - console.warn('Failed to download preview:', e); - } - }; - - cancelResize.onclick = closeResizeDialog; - - resizeOverlay.style.display = 'block'; - resizeContainer.style.display = 'block'; - - // Reinitialize color palette with current available colors - initializeColorPalette(resizeContainer); - - _updateResizePreview(); - _resizeDialogCleanup = () => { - try { - zoomSlider.replaceWith(zoomSlider.cloneNode(true)); - } catch {} - try { - if (zoomInBtn) zoomInBtn.replaceWith(zoomInBtn.cloneNode(true)); - } catch {} - try { - if (zoomOutBtn) zoomOutBtn.replaceWith(zoomOutBtn.cloneNode(true)); - } catch {} - }; - setTimeout(() => { - if (typeof computeFitZoom === 'function') { - const z = computeFitZoom(); - if (!isNaN(z) && isFinite(z)) { - applyZoom(z); - centerInView(); - } - } else { - centerInView(); - } - }, 0); - } - - function closeResizeDialog() { - try { - if (typeof _resizeDialogCleanup === 'function') { - _resizeDialogCleanup(); - } - } catch {} - resizeOverlay.style.display = 'none'; - resizeContainer.style.display = 'none'; - _updateResizePreview = () => {}; - try { - if (typeof cancelAnimationFrame === 'function' && _panRaf) { - cancelAnimationFrame(_panRaf); - } - } catch {} - try { - if (_previewTimer) { - clearTimeout(_previewTimer); - _previewTimer = null; - } - } catch {} - _maskImageData = null; - _maskData = null; - _dirty = null; - _ditherWorkBuf = null; - _ditherEligibleBuf = null; - _resizeDialogCleanup = null; - } - - if (uploadBtn) { - uploadBtn.addEventListener('click', async () => { - const availableColors = Utils.extractAvailableColors(); - if (availableColors === null || availableColors.length < 10) { - updateUI('noColorsFound', 'error'); - Utils.showAlert(Utils.t('noColorsFound'), 'error'); - return; - } - - if (!state.colorsChecked) { - state.availableColors = availableColors; - state.colorsChecked = true; - updateUI('colorsFound', 'success', { count: availableColors.length }); - updateStats(); - selectPosBtn.disabled = false; - // Only enable resize button if image is also loaded - if (state.imageLoaded) { - resizeBtn.disabled = false; - } - } - - try { - updateUI('loadingImage', 'default'); - const imageSrc = await Utils.createImageUploader(); - if (!imageSrc) { - updateUI('colorsFound', 'success', { - count: state.availableColors.length, - }); - return; - } - - const processor = new ImageProcessor(imageSrc); - await processor.load(); - - const { width, height } = processor.getDimensions(); - const pixels = processor.getPixelData(); - - let totalValidPixels = 0; - for (let i = 0; i < pixels.length; i += 4) { - const isTransparent = - !state.paintTransparentPixels && - pixels[i + 3] < (state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD); - const isWhiteAndSkipped = - !state.paintWhitePixels && - Utils.isWhitePixel(pixels[i], pixels[i + 1], pixels[i + 2]); - if (!isTransparent && !isWhiteAndSkipped) { - totalValidPixels++; - } - } - - state.imageData = { - width, - height, - pixels, - totalPixels: totalValidPixels, - processor, - }; - - state.totalPixels = totalValidPixels; - state.paintedPixels = 0; - state.imageLoaded = true; - state.lastPosition = { x: 0, y: 0 }; - - // Initialize painted map for tracking - Utils.initializePaintedMap(width, height); - - // New image: clear previous resize settings - state.resizeSettings = null; - // Also clear any previous ignore mask - state.resizeIgnoreMask = null; - // Save original image for this browser (dataUrl + dims) - state.originalImage = { dataUrl: imageSrc, width, height }; - saveBotSettings(); - - // Use the original image for the overlay initially - const imageBitmap = await createImageBitmap(processor.img); - await overlayManager.setImage(imageBitmap); - overlayManager.enable(); - toggleOverlayBtn.disabled = false; - toggleOverlayBtn.classList.add('active'); - toggleOverlayBtn.setAttribute('aria-pressed', 'true'); - - // Only enable resize button if colors have also been captured - if (state.colorsChecked) { - resizeBtn.disabled = false; - } - saveBtn.disabled = false; - - if (state.startPosition) { - startBtn.disabled = false; - } - - updateStats(); - updateDataButtons(); - updateUI('imageLoaded', 'success', { count: totalValidPixels }); - } catch { - updateUI('imageError', 'error'); - } - }); - } - - if (resizeBtn) { - resizeBtn.addEventListener('click', () => { - if (state.imageLoaded && state.imageData.processor && state.colorsChecked) { - showResizeDialog(state.imageData.processor); - } else if (!state.colorsChecked) { - Utils.showAlert(Utils.t('uploadImageFirstColors'), 'warning'); - } - }); - } - - if (selectPosBtn) { - selectPosBtn.addEventListener('click', async () => { - if (state.selectingPosition) return; - - state.selectingPosition = true; - state.startPosition = null; - state.region = null; - startBtn.disabled = true; - - Utils.showAlert(Utils.t('selectPositionAlert'), 'info'); - updateUI('waitingPosition', 'default'); - - const tempFetch = async (url, options) => { - if ( - typeof url === 'string' && - url.includes('https://backend.wplace.live/s0/pixel/') && - options?.method?.toUpperCase() === 'POST' - ) { - try { - const response = await originalFetch(url, options); - const clonedResponse = response.clone(); - const data = await clonedResponse.json(); - - if (data?.painted === 1) { - const regionMatch = url.match(/\/pixel\/(\d+)\/(\d+)/); - if (regionMatch && regionMatch.length >= 3) { - state.region = { - x: Number.parseInt(regionMatch[1]), - y: Number.parseInt(regionMatch[2]), - }; - } - - const payload = JSON.parse(options.body); - if (payload?.coords && Array.isArray(payload.coords)) { - state.startPosition = { - x: payload.coords[0], - y: payload.coords[1], - }; - state.lastPosition = { x: 0, y: 0 }; - - await overlayManager.setPosition(state.startPosition, state.region); - - if (state.imageLoaded) { - startBtn.disabled = false; - } - - window.fetch = originalFetch; - state.selectingPosition = false; - updateUI('positionSet', 'success'); - } - } - - return response; - } catch { - return originalFetch(url, options); - } - } - return originalFetch(url, options); - }; - - const originalFetch = window.fetch; - window.fetch = tempFetch; - - setTimeout(() => { - if (state.selectingPosition) { - window.fetch = originalFetch; - state.selectingPosition = false; - updateUI('positionTimeout', 'error'); - Utils.showAlert(Utils.t('positionTimeout'), 'error'); - } - }, 120000); - }); - } - - async function startPainting() { - if (!state.imageLoaded || !state.startPosition || !state.region) { - updateUI('missingRequirements', 'error'); - return; - } - await ensureToken(); - if (!turnstileToken) return; - - state.running = true; - state.stopFlag = false; - startBtn.disabled = true; - stopBtn.disabled = false; - uploadBtn.disabled = true; - selectPosBtn.disabled = true; - resizeBtn.disabled = true; - saveBtn.disabled = true; - toggleOverlayBtn.disabled = true; - - updateUI('startPaintingMsg', 'success'); - - try { - await processImage(); - } catch (e) { - console.error('Unexpected error:', e); - updateUI('paintingError', 'error'); - } finally { - state.running = false; - stopBtn.disabled = true; - saveBtn.disabled = false; - - if (state.stopFlag) { - startBtn.disabled = false; - } else { - startBtn.disabled = true; - uploadBtn.disabled = false; - selectPosBtn.disabled = false; - resizeBtn.disabled = false; - } - toggleOverlayBtn.disabled = false; - } - } - - if (startBtn) { - startBtn.addEventListener('click', startPainting); - } - - if (stopBtn) { - stopBtn.addEventListener('click', () => { - state.stopFlag = true; - state.running = false; - stopBtn.disabled = true; - updateUI('paintingStoppedByUser', 'warning'); - - if (state.imageLoaded && state.paintedPixels > 0) { - Utils.saveProgress(); - Utils.showAlert(Utils.t('autoSaved'), 'success'); - } - }); - } - - const checkSavedProgress = () => { - const savedData = Utils.loadProgress(); - if (savedData && savedData.state.paintedPixels > 0) { - const savedDate = new Date(savedData.timestamp).toLocaleString(); - const progress = Math.round( - (savedData.state.paintedPixels / savedData.state.totalPixels) * 100 - ); - - Utils.showAlert( - `${Utils.t('savedDataFound')}\n\n` + - `Saved: ${savedDate}\n` + - `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + - `${Utils.t('clickLoadToContinue')}`, - 'info' - ); - } - }; - - setTimeout(checkSavedProgress, 1000); - - if (cooldownSlider && cooldownInput && cooldownValue && cooldownDecrease && cooldownIncrease) { - const updateCooldown = (newValue) => { - const threshold = Math.max(1, Math.min(state.maxCharges || 999, parseInt(newValue))); - state.cooldownChargeThreshold = threshold; - - // Update both controls (value shows in input, label shows unit only) - cooldownSlider.value = threshold; - cooldownInput.value = threshold; - cooldownValue.textContent = `${Utils.t('charges')}`; - - saveBotSettings(); - NotificationManager.resetEdgeTracking(); // prevent spurious notify after threshold change - }; - - // Slider event listener - cooldownSlider.addEventListener('input', (e) => { - updateCooldown(e.target.value); - }); - - // Number input event listener - cooldownInput.addEventListener('input', (e) => { - updateCooldown(e.target.value); - }); - - // Decrease button - cooldownDecrease.addEventListener('click', () => { - updateCooldown(parseInt(cooldownInput.value) - 1); - }); - - // Increase button - cooldownIncrease.addEventListener('click', () => { - updateCooldown(parseInt(cooldownInput.value) + 1); - }); - - // Add scroll-to-adjust for cooldown slider - Utils.createScrollToAdjust(cooldownSlider, updateCooldown, 1, state.maxCharges, 1); - } - - loadBotSettings(); - // Ensure notification poller reflects current settings - NotificationManager.syncFromState(); - } - - function getMsToTargetCharges(current, target, cooldown, intervalMs = 0) { - const remainingCharges = target - current; - return Math.max(0, remainingCharges * cooldown - intervalMs); - } - - function updateChargesThresholdUI(intervalMs) { - if (state.stopFlag) return; - - const threshold = state.cooldownChargeThreshold; - const remainingMs = getMsToTargetCharges( - state.preciseCurrentCharges, - threshold, - state.cooldown, - intervalMs - ); - const timeText = Utils.msToTimeText(remainingMs); - - updateUI( - 'noChargesThreshold', - 'warning', - { - threshold, - current: state.displayCharges, - time: timeText, - }, - true - ); - } - - function generateCoordinates(width, height, mode, direction, snake, blockWidth, blockHeight) { - const coords = []; - console.log( - 'Generating coordinates with \n mode:', - mode, - '\n direction:', - direction, - '\n snake:', - snake, - '\n blockWidth:', - blockWidth, - '\n blockHeight:', - blockHeight - ); - // --------- Standard 4 corners traversal ---------- - let xStart, xEnd, xStep; - let yStart, yEnd, yStep; - switch (direction) { - case 'top-left': - xStart = 0; - xEnd = width; - xStep = 1; - yStart = 0; - yEnd = height; - yStep = 1; - break; - case 'top-right': - xStart = width - 1; - xEnd = -1; - xStep = -1; - yStart = 0; - yEnd = height; - yStep = 1; - break; - case 'bottom-left': - xStart = 0; - xEnd = width; - xStep = 1; - yStart = height - 1; - yEnd = -1; - yStep = -1; - break; - case 'bottom-right': - xStart = width - 1; - xEnd = -1; - xStep = -1; - yStart = height - 1; - yEnd = -1; - yStep = -1; - break; - default: - throw new Error(`Unknown direction: ${direction}`); - } - - // --------- Traversal modes ---------- - if (mode === 'rows') { - for (let y = yStart; y !== yEnd; y += yStep) { - if (snake && (y - yStart) % 2 !== 0) { - for (let x = xEnd - xStep; x !== xStart - xStep; x -= xStep) { - coords.push([x, y]); - } - } else { - for (let x = xStart; x !== xEnd; x += xStep) { - coords.push([x, y]); - } - } - } - } else if (mode === 'columns') { - for (let x = xStart; x !== xEnd; x += xStep) { - if (snake && (x - xStart) % 2 !== 0) { - for (let y = yEnd - yStep; y !== yStart - yStep; y -= yStep) { - coords.push([x, y]); - } - } else { - for (let y = yStart; y !== yEnd; y += yStep) { - coords.push([x, y]); - } - } - } - } else if (mode === 'circle-out') { - const cx = Math.floor(width / 2); - const cy = Math.floor(height / 2); - const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy)); - - for (let r = 0; r <= maxRadius; r++) { - for (let y = cy - r; y <= cy + r; y++) { - for (let x = cx - r; x <= cx + r; x++) { - if (x >= 0 && x < width && y >= 0 && y < height) { - const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy)); - if (dist === r) coords.push([x, y]); - } - } - } - } - } else if (mode === 'circle-in') { - const cx = Math.floor(width / 2); - const cy = Math.floor(height / 2); - const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy)); - - for (let r = maxRadius; r >= 0; r--) { - for (let y = cy - r; y <= cy + r; y++) { - for (let x = cx - r; x <= cx + r; x++) { - if (x >= 0 && x < width && y >= 0 && y < height) { - const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy)); - if (dist === r) coords.push([x, y]); - } - } - } - } - } else if (mode === 'blocks' || mode === 'shuffle-blocks') { - const blocks = []; - for (let by = 0; by < height; by += blockHeight) { - for (let bx = 0; bx < width; bx += blockWidth) { - const block = []; - for (let y = by; y < Math.min(by + blockHeight, height); y++) { - for (let x = bx; x < Math.min(bx + blockWidth, width); x++) { - block.push([x, y]); - } - } - blocks.push(block); - } - } - - if (mode === 'shuffle-blocks') { - // Simple Fisher-Yates shuffle - for (let i = blocks.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [blocks[i], blocks[j]] = [blocks[j], blocks[i]]; - } - } - - // Concatenate all blocks - for (const block of blocks) { - coords.push(...block); - } - } else { - throw new Error(`Unknown mode: ${mode}`); - } - - return coords; - } - - async function flushPixelBatch(pixelBatch) { - if (!pixelBatch || pixelBatch.pixels.length === 0) return true; - - const batchSize = pixelBatch.pixels.length; - console.log( - `📦 Sending batch with ${batchSize} pixels (region: ${pixelBatch.regionX},${pixelBatch.regionY})` - ); - const success = await sendBatchWithRetry( - pixelBatch.pixels, - pixelBatch.regionX, - pixelBatch.regionY - ); - if (success) { - pixelBatch.pixels.forEach((p) => { - state.paintedPixels++; - Utils.markPixelPainted(p.x, p.y, pixelBatch.regionX, pixelBatch.regionY); - }); - state.fullChargeData = { - ...state.fullChargeData, - spentSinceShot: state.fullChargeData.spentSinceShot + batchSize, - }; - updateStats(); - updateUI('paintingProgress', 'default', { - painted: state.paintedPixels, - total: state.totalPixels, - }); - Utils.performSmartSave(); - - if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && batchSize > 0) { - const delayPerPixel = 1000 / state.paintingSpeed; - const totalDelay = Math.max(100, delayPerPixel * batchSize); - await Utils.sleep(totalDelay); - } - } else { - console.error(`❌ Batch failed permanently after retries. Stopping painting.`); - state.stopFlag = true; - updateUI('paintingBatchFailed', 'error'); - } - - pixelBatch.pixels = []; - return success; - } - - async function processImage() { - const { width, height, pixels } = state.imageData; - const { x: startX, y: startY } = state.startPosition; - const { x: regionX, y: regionY } = state.region; - - // todo force load tiles - const tilesReady = await overlayManager.waitForTiles( - regionX, - regionY, - width, - height, - startX, - startY, - 10000 // timeout 10s - ); - - if (!tilesReady) { - updateUI('overlayTilesNotLoaded', 'error'); - state.stopFlag = true; - return; - } - - let pixelBatch = null; - let skippedPixels = { - transparent: 0, - white: 0, - alreadyPainted: 0, - colorUnavailable: 0, - }; - - const transparencyThreshold = - state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - - function checkPixelEligibility(x, y) { - const idx = (y * width + x) * 4; - const r = pixels[idx], - g = pixels[idx + 1], - b = pixels[idx + 2], - a = pixels[idx + 3]; - - if (!state.paintTransparentPixels && a < transparencyThreshold) - return { - eligible: false, - reason: 'transparent', - }; - if (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) - return { - eligible: false, - reason: 'white', - }; - - let targetRgb = Utils.isWhitePixel(r, g, b) - ? [255, 255, 255] - : Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); - - // Template color ID, normalized/mapped to the nearest available color in our palette. - // Example: template requires "Slate", but we only have "Dark Gray" available - // → mappedTargetColorId = ID of Dark Gray. - // - // If `state.paintUnavailablePixels` is enabled, the painting would stop earlier - // because "Slate" was not found (null returned). - // - // Else, the template "Slate" is mapped to the closest available color (e.g., "Dark Gray"), - // and we proceed with painting using that mapped color. - // - // In this case, if the canvas pixel is already Slate (mapped to available Dark Gray), - // we skip painting, since template and canvas both resolve to the same available color (Dark Gray). - const mappedTargetColorId = Utils.resolveColor( - targetRgb, - state.availableColors, - !state.paintUnavailablePixels - ); - - // Technically, checking only `!mappedTargetColorId.id` would be enough, - // but combined with `state.paintUnavailablePixels` it makes the logic explicit: - // we only skip when the template color cannot be mapped AND strict mode is on. - if (!state.paintUnavailablePixels && !mappedTargetColorId.id) { - return { - eligible: false, - reason: 'colorUnavailable', - r, - g, - b, - a, - mappedColorId: mappedTargetColorId.id, - }; - } - return { eligible: true, r, g, b, a, mappedColorId: mappedTargetColorId.id }; - } - - function skipPixel(reason, id, rgb, x, y) { - if (reason !== 'transparent') { - console.log(`Skipped pixel for ${reason} (id: ${id}, (${rgb.join(', ')})) at (${x}, ${y})`); - } - skippedPixels[reason]++; - } - - try { - const coords = generateCoordinates( - width, - height, - state.coordinateMode, - state.coordinateDirection, - state.coordinateSnake, - state.blockWidth, - state.blockHeight - ); - - outerLoop: for (const [x, y] of coords) { - if (state.stopFlag) { - if (pixelBatch && pixelBatch.pixels.length > 0) { - console.log( - `🎯 Sending last batch before stop with ${pixelBatch.pixels.length} pixels` - ); - await flushPixelBatch(pixelBatch); - } - state.lastPosition = { x, y }; - updateUI('paintingPaused', 'warning', { x, y }); - // noinspection UnnecessaryLabelOnBreakStatementJS - break outerLoop; - } - - const targetPixelInfo = checkPixelEligibility(x, y); - let absX = startX + x; - let absY = startY + y; - - let adderX = Math.floor(absX / 1000); - let adderY = Math.floor(absY / 1000); - let pixelX = absX % 1000; - let pixelY = absY % 1000; - - // Template color ID, normalized/mapped to the nearest available color in our palette. - // Example: template requires "Slate", but we only have "Dark Gray" available - // → mappedTargetColorId = ID of Dark Gray. - // - // If `state.paintUnavailablePixels` is enabled, the painting would stop earlier - // because "Slate" was not found (null returned). - // - // Else, the template "Slate" is mapped to the closest available color (e.g., "Dark Gray"), - // and we proceed with painting using that mapped color. - // - // In this case, if the canvas pixel is already Slate (mapped to available Dark Gray), - // we skip painting, since template and canvas both resolve to the same available color (Dark Gray). - const targetMappedColorId = targetPixelInfo.mappedColorId; - - if (!targetPixelInfo.eligible) { - skipPixel( - targetPixelInfo.reason, - targetMappedColorId, - [targetPixelInfo.r, targetPixelInfo.g, targetPixelInfo.b], - pixelX, - pixelY - ); - continue; - } - - // console.log(`[DEBUG] Pixel at (${pixelX}, ${pixelY}) eligible: RGB=${targetPixelInfo.r}, ${targetPixelInfo.g}, ${targetPixelInfo.b}, - // alpha=${targetPixelInfo.a}, mappedColorId=${targetMappedColorId}`); - - if ( - !pixelBatch || - pixelBatch.regionX !== regionX + adderX || - pixelBatch.regionY !== regionY + adderY - ) { - if (pixelBatch && pixelBatch.pixels.length > 0) { - console.log( - `🌍 Sending region-change batch with ${pixelBatch.pixels.length} pixels (switching to region ${ - regionX + adderX - },${regionY + adderY})` - ); - const success = await flushPixelBatch(pixelBatch); - - if (success) { - if ( - CONFIG.PAINTING_SPEED_ENABLED && - state.paintingSpeed > 0 && - pixelBatch.pixels.length > 0 - ) { - const batchDelayFactor = Math.max(1, 100 / state.paintingSpeed); - const totalDelay = Math.max(100, batchDelayFactor * pixelBatch.pixels.length); - await Utils.sleep(totalDelay); - } - updateStats(); - } else { - console.error(`❌ Batch failed permanently after retries. Stopping painting.`); - state.stopFlag = true; - updateUI('paintingBatchFailed', 'error'); - // noinspection UnnecessaryLabelOnBreakStatementJS - break outerLoop; - } - } - - pixelBatch = { - regionX: regionX + adderX, - regionY: regionY + adderY, - pixels: [], - }; - } - - try { - const tileKeyParts = [pixelBatch.regionX, pixelBatch.regionY]; - - const tilePixelRGBA = await overlayManager.getTilePixelColor( - tileKeyParts[0], - tileKeyParts[1], - pixelX, - pixelY - ); - - if (tilePixelRGBA && Array.isArray(tilePixelRGBA)) { - // Resolve the actual canvas pixel color to the closest available color. - // (The raw canvas RGB [er, eg, eb] is mapped into state.availableColors) - // so that comparison is consistent with targetMappedColorId. - const mappedCanvasColor = Utils.resolveColor( - tilePixelRGBA.slice(0, 3), - state.availableColors - ); - const isMatch = mappedCanvasColor.id === targetMappedColorId; - if (isMatch) { - skipPixel( - 'alreadyPainted', - targetMappedColorId, - [targetPixelInfo.r, targetPixelInfo.g, targetPixelInfo.b], - pixelX, - pixelY - ); - continue; - } - console.debug( - `[COMPARE] Pixel at 📍 (${pixelX}, ${pixelY}) in region (${ - regionX + adderX - }, ${regionY + adderY})\n` + - ` ├── Current color: rgb(${tilePixelRGBA.slice(0, 3).join(', ')}) (id: ${mappedCanvasColor.id})\n` + - ` ├── Target color: rgb(${targetPixelInfo.r}, ${targetPixelInfo.g}, ${targetPixelInfo.b}) (id: ${targetMappedColorId})\n` + - ` └── Status: ${ - isMatch ? '✅ Already painted → SKIP' : '🔴 Needs paint → PAINT' - }\n` - ); - } - } catch (e) { - console.error(`[DEBUG] Error checking existing pixel at (${pixelX}, ${pixelY}):`, e); - updateUI('paintingPixelCheckFailed', 'error', { x: pixelX, y: pixelY }); - state.stopFlag = true; - // noinspection UnnecessaryLabelOnBreakStatementJS - break outerLoop; - } - - pixelBatch.pixels.push({ - x: pixelX, - y: pixelY, - color: targetMappedColorId, - localX: x, - localY: y, - }); - - const maxBatchSize = calculateBatchSize(); - if (pixelBatch.pixels.length >= maxBatchSize) { - const modeText = - state.batchMode === 'random' - ? `random (${state.randomBatchMin}-${state.randomBatchMax})` - : 'normal'; - console.log( - `📦 Sending batch with ${pixelBatch.pixels.length} pixels (mode: ${modeText}, target: ${maxBatchSize})` - ); - const success = await flushPixelBatch(pixelBatch); - if (!success) { - console.error(`❌ Batch failed permanently after retries. Stopping painting.`); - state.stopFlag = true; - updateUI('paintingBatchFailed', 'error'); - // noinspection UnnecessaryLabelOnBreakStatementJS - break outerLoop; - } - - pixelBatch.pixels = []; - } - - if (state.displayCharges < state.cooldownChargeThreshold && !state.stopFlag) { - await Utils.dynamicSleep(() => { - if (state.displayCharges >= state.cooldownChargeThreshold) { - NotificationManager.maybeNotifyChargesReached(true); - return 0; - } - if (state.stopFlag) return 0; - return getMsToTargetCharges( - state.preciseCurrentCharges, - state.cooldownChargeThreshold, - state.cooldown - ); - }); - } - - if (state.stopFlag) { - // noinspection UnnecessaryLabelOnBreakStatementJS - break outerLoop; - } - } - - if (pixelBatch && pixelBatch.pixels.length > 0 && !state.stopFlag) { - console.log(`🏁 Sending final batch with ${pixelBatch.pixels.length} pixels`); - const success = await flushPixelBatch(pixelBatch); - if (!success) { - console.warn( - `⚠️ Final batch failed with ${pixelBatch.pixels.length} pixels after all retries.` - ); - } - } - } finally { - if (window._chargesInterval) clearInterval(window._chargesInterval); - window._chargesInterval = null; - } - - if (state.stopFlag) { - // Save progress when stopped to preserve painted map - Utils.saveProgress(); - } else { - updateUI('paintingComplete', 'success', { count: state.paintedPixels }); - state.lastPosition = { x: 0, y: 0 }; - // Keep painted map until user starts new project - // state.paintedMap = null // Commented out to preserve data - Utils.saveProgress(); // Save final complete state - overlayManager.clear(); - const toggleOverlayBtn = document.getElementById('toggleOverlayBtn'); - if (toggleOverlayBtn) { - toggleOverlayBtn.classList.remove('active'); - toggleOverlayBtn.disabled = true; - } - } - - // Log skip statistics - console.log(`📊 Pixel Statistics:`); - console.log(` Painted: ${state.paintedPixels}`); - console.log(` Skipped - Transparent: ${skippedPixels.transparent}`); - console.log(` Skipped - White (disabled): ${skippedPixels.white}`); - console.log(` Skipped - Already painted: ${skippedPixels.alreadyPainted}`); - console.log(` Skipped - Color Unavailable: ${skippedPixels.colorUnavailable}`); - console.log( - ` Total processed: ${ - state.paintedPixels + - skippedPixels.transparent + - skippedPixels.white + - skippedPixels.alreadyPainted + - skippedPixels.colorUnavailable - }` - ); - - updateStats(); - } - - // Helper function to calculate batch size based on mode - function calculateBatchSize() { - let targetBatchSize; - - if (state.batchMode === 'random') { - // Generate random batch size within the specified range - const min = Math.max(1, state.randomBatchMin); - const max = Math.max(min, state.randomBatchMax); - targetBatchSize = Math.floor(Math.random() * (max - min + 1)) + min; - console.log(`🎲 Random batch size generated: ${targetBatchSize} (range: ${min}-${max})`); - } else { - // Normal mode - use the fixed paintingSpeed value - targetBatchSize = state.paintingSpeed; - } - - // Always limit by available charges - const maxAllowed = state.displayCharges; - const finalBatchSize = Math.min(targetBatchSize, maxAllowed); - - return finalBatchSize; - } - - // Helper function to retry batch until success with exponential backoff - async function sendBatchWithRetry(pixels, regionX, regionY, maxRetries = MAX_BATCH_RETRIES) { - let attempt = 0; - while (attempt < maxRetries && !state.stopFlag) { - attempt++; - console.log( - `🔄 Attempting to send batch (attempt ${attempt}/${maxRetries}) for region ${regionX},${regionY} with ${pixels.length} pixels` - ); - - const result = await sendPixelBatch(pixels, regionX, regionY); - - if (result === true) { - console.log(`✅ Batch succeeded on attempt ${attempt}`); - return true; - } else if (result === 'token_error') { - console.log(`🔑 Token error on attempt ${attempt}, regenerating...`); - updateUI('captchaSolving', 'warning'); - try { - await handleCaptcha(); - // Don't count token regeneration as a failed attempt - attempt--; - continue; - } catch (e) { - console.error(`❌ Token regeneration failed on attempt ${attempt}:`, e); - updateUI('captchaFailed', 'error'); - // Wait longer before retrying after token failure - await Utils.sleep(5000); - } - } else { - console.warn(`⚠️ Batch failed on attempt ${attempt}, retrying...`); - // Exponential backoff with jitter - const baseDelay = Math.min(1000 * Math.pow(2, attempt - 1), 30000); // Max 30s - const jitter = Math.random() * 1000; // Add up to 1s random delay - await Utils.sleep(baseDelay + jitter); - } - } - - if (attempt >= maxRetries) { - console.error( - `❌ Batch failed after ${maxRetries} attempts (MAX_BATCH_RETRIES=${MAX_BATCH_RETRIES}). This will stop painting to prevent infinite loops.` - ); - updateUI('paintingError', 'error'); - return false; - } - - return false; - } - - async function sendPixelBatch(pixelBatch, regionX, regionY) { - let token = turnstileToken; - - // Generate new token if we don't have one - if (!token) { - try { - console.log('🔑 Generating Turnstile token for pixel batch...'); - token = await handleCaptcha(); - turnstileToken = token; // Store for potential reuse - } catch (error) { - console.error('❌ Failed to generate Turnstile token:', error); - tokenPromise = new Promise((resolve) => { - _resolveToken = resolve; - }); - return 'token_error'; - } - } - - const coords = new Array(pixelBatch.length * 2); - const colors = new Array(pixelBatch.length); - for (let i = 0; i < pixelBatch.length; i++) { - const pixel = pixelBatch[i]; - coords[i * 2] = pixel.x; - coords[i * 2 + 1] = pixel.y; - colors[i] = pixel.color; - } - - try { - const payload = { coords, colors, t: token }; - - const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { - method: 'POST', - headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, - credentials: 'include', - body: JSON.stringify(payload), - }); - - if (res.status === 403) { - let data = null; - try { - data = await res.json(); - } catch (_) {} - console.error('❌ 403 Forbidden. Turnstile token might be invalid or expired.'); - - // Try to generate a new token and retry once - try { - console.log('🔄 Regenerating Turnstile token after 403...'); - token = await handleCaptcha(); - turnstileToken = token; - - // Retry the request with new token - const retryPayload = { coords, colors, t: token }; - const retryRes = await fetch( - `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, - { - method: 'POST', - headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, - credentials: 'include', - body: JSON.stringify(retryPayload), - } - ); - - if (retryRes.status === 403) { - turnstileToken = null; - tokenPromise = new Promise((resolve) => { - _resolveToken = resolve; - }); - return 'token_error'; - } - - const retryData = await retryRes.json(); - return retryData?.painted === pixelBatch.length; - } catch (retryError) { - console.error('❌ Token regeneration failed:', retryError); - turnstileToken = null; - tokenPromise = new Promise((resolve) => { - _resolveToken = resolve; - }); - return 'token_error'; - } - } - - const data = await res.json(); - return data?.painted === pixelBatch.length; - } catch (e) { - console.error('Batch paint request failed:', e); - return false; - } - } - - function saveBotSettings() { - try { - const settings = { - paintingSpeed: state.paintingSpeed, - paintingSpeedEnabled: document.getElementById('enableSpeedToggle')?.checked, - batchMode: state.batchMode, // "normal" or "random" - randomBatchMin: state.randomBatchMin, - randomBatchMax: state.randomBatchMax, - cooldownChargeThreshold: state.cooldownChargeThreshold, - tokenSource: state.tokenSource, // "generator", "hybrid", or "manual" - minimized: state.minimized, - overlayOpacity: state.overlayOpacity, - blueMarbleEnabled: document.getElementById('enableBlueMarbleToggle')?.checked, - ditheringEnabled: state.ditheringEnabled, - colorMatchingAlgorithm: state.colorMatchingAlgorithm, - enableChromaPenalty: state.enableChromaPenalty, - chromaPenaltyWeight: state.chromaPenaltyWeight, - customTransparencyThreshold: state.customTransparencyThreshold, - customWhiteThreshold: state.customWhiteThreshold, - paintWhitePixels: state.paintWhitePixels, - paintTransparentPixels: state.paintTransparentPixels, - resizeSettings: state.resizeSettings, - paintUnavailablePixels: state.paintUnavailablePixels, - coordinateMode: state.coordinateMode, - coordinateDirection: state.coordinateDirection, - coordinateSnake: state.coordinateSnake, - blockWidth: state.blockWidth, - blockHeight: state.blockHeight, // Save ignore mask (as base64) with its dimensions - resizeIgnoreMask: - state.resizeIgnoreMask && - state.resizeSettings && - state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length - ? { - w: state.resizeSettings.width, - h: state.resizeSettings.height, - data: btoa(String.fromCharCode(...state.resizeIgnoreMask)), - } - : null, // Notifications - notificationsEnabled: state.notificationsEnabled, - notifyOnChargesReached: state.notifyOnChargesReached, - notifyOnlyWhenUnfocused: state.notifyOnlyWhenUnfocused, - notificationIntervalMinutes: state.notificationIntervalMinutes, - originalImage: state.originalImage, - }; - CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled; - // AUTO_CAPTCHA_ENABLED is always true - no need to save/load - - localStorage.setItem('wplace-bot-settings', JSON.stringify(settings)); - } catch (e) { - console.warn('Could not save bot settings:', e); - } - } - - function loadBotSettings() { - try { - const saved = localStorage.getItem('wplace-bot-settings'); - if (!saved) return; - const settings = JSON.parse(saved); - - state.paintingSpeed = settings.paintingSpeed || CONFIG.PAINTING_SPEED.DEFAULT; - state.batchMode = settings.batchMode || CONFIG.BATCH_MODE; // Default to "normal" - state.randomBatchMin = settings.randomBatchMin || CONFIG.RANDOM_BATCH_RANGE.MIN; - state.randomBatchMax = settings.randomBatchMax || CONFIG.RANDOM_BATCH_RANGE.MAX; - state.cooldownChargeThreshold = - settings.cooldownChargeThreshold || CONFIG.COOLDOWN_CHARGE_THRESHOLD; - state.tokenSource = settings.tokenSource || CONFIG.TOKEN_SOURCE; // Default to "generator" - state.minimized = settings.minimized ?? false; - CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled ?? false; - CONFIG.AUTO_CAPTCHA_ENABLED = settings.autoCaptchaEnabled ?? false; - state.overlayOpacity = settings.overlayOpacity ?? CONFIG.OVERLAY.OPACITY_DEFAULT; - state.blueMarbleEnabled = settings.blueMarbleEnabled ?? CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT; - state.ditheringEnabled = settings.ditheringEnabled ?? false; - state.colorMatchingAlgorithm = settings.colorMatchingAlgorithm || 'lab'; - state.enableChromaPenalty = settings.enableChromaPenalty ?? true; - state.chromaPenaltyWeight = settings.chromaPenaltyWeight ?? 0.15; - state.customTransparencyThreshold = - settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; - state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; - state.paintWhitePixels = settings.paintWhitePixels ?? true; - state.paintTransparentPixels = settings.paintTransparentPixels ?? false; - state.resizeSettings = settings.resizeSettings ?? null; - state.originalImage = settings.originalImage ?? null; - state.paintUnavailablePixels = settings.paintUnavailablePixels ?? CONFIG.PAINT_UNAVAILABLE; - state.coordinateMode = settings.coordinateMode ?? CONFIG.COORDINATE_MODE; - state.coordinateDirection = settings.coordinateDirection ?? CONFIG.COORDINATE_DIRECTION; - state.coordinateSnake = settings.coordinateSnake ?? CONFIG.COORDINATE_SNAKE; - state.blockWidth = settings.blockWidth ?? CONFIG.COORDINATE_BLOCK_WIDTH; - state.blockHeight = settings.blockHeight ?? CONFIG.COORDINATE_BLOCK_HEIGHT; - // Notifications - state.notificationsEnabled = settings.notificationsEnabled ?? CONFIG.NOTIFICATIONS.ENABLED; - state.notifyOnChargesReached = - settings.notifyOnChargesReached ?? CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED; - state.notifyOnlyWhenUnfocused = - settings.notifyOnlyWhenUnfocused ?? CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED; - state.notificationIntervalMinutes = - settings.notificationIntervalMinutes ?? CONFIG.NOTIFICATIONS.REPEAT_MINUTES; - // Restore ignore mask if dims match current resizeSettings - if ( - settings.resizeIgnoreMask && - settings.resizeIgnoreMask.data && - state.resizeSettings && - settings.resizeIgnoreMask.w === state.resizeSettings.width && - settings.resizeIgnoreMask.h === state.resizeSettings.height - ) { - try { - const bin = atob(settings.resizeIgnoreMask.data); - const arr = new Uint8Array(bin.length); - for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i); - state.resizeIgnoreMask = arr; - } catch { - state.resizeIgnoreMask = null; - } - } else { - state.resizeIgnoreMask = null; - } - // Initialize coordinate generation UI - const coordinateModeSelect = document.getElementById('coordinateModeSelect'); - if (coordinateModeSelect) coordinateModeSelect.value = state.coordinateMode; - - const coordinateDirectionSelect = document.getElementById('coordinateDirectionSelect'); - if (coordinateDirectionSelect) coordinateDirectionSelect.value = state.coordinateDirection; - - const coordinateSnakeToggle = document.getElementById('coordinateSnakeToggle'); - if (coordinateSnakeToggle) coordinateSnakeToggle.checked = state.coordinateSnake; - - const settingsContainer = document.getElementById('wplace-settings-container'); - const directionControls = settingsContainer.querySelector('#directionControls'); - const snakeControls = settingsContainer.querySelector('#snakeControls'); - const blockControls = settingsContainer.querySelector('#blockControls'); - Utils.updateCoordinateUI({ - mode: state.coordinateMode, - directionControls, - snakeControls, - blockControls, - }); - - const paintUnavailablePixelsToggle = document.getElementById('paintUnavailablePixelsToggle'); - if (paintUnavailablePixelsToggle) { - paintUnavailablePixelsToggle.checked = state.paintUnavailablePixels; - } - - const settingsPaintWhiteToggle = settingsContainer.querySelector('#settingsPaintWhiteToggle'); - if (settingsPaintWhiteToggle) { - settingsPaintWhiteToggle.checked = state.paintWhitePixels; - } - - const settingsPaintTransparentToggle = settingsContainer.querySelector( - '#settingsPaintTransparentToggle' - ); - if (settingsPaintTransparentToggle) { - settingsPaintTransparentToggle.checked = state.paintTransparentPixels; - } - - const speedSlider = document.getElementById('speedSlider'); - const speedInput = document.getElementById('speedInput'); - const speedValue = document.getElementById('speedValue'); - if (speedSlider) speedSlider.value = state.paintingSpeed; - if (speedInput) speedInput.value = state.paintingSpeed; - if (speedValue) speedValue.textContent = `pixels`; - - const enableSpeedToggle = document.getElementById('enableSpeedToggle'); - if (enableSpeedToggle) enableSpeedToggle.checked = CONFIG.PAINTING_SPEED_ENABLED; - - // Batch mode UI initialization - const batchModeSelect = document.getElementById('batchModeSelect'); - if (batchModeSelect) batchModeSelect.value = state.batchMode; - - const normalBatchControls = document.getElementById('normalBatchControls'); - const randomBatchControls = document.getElementById('randomBatchControls'); - - // Show/hide appropriate controls based on batch mode - if (normalBatchControls && randomBatchControls) { - if (state.batchMode === 'random') { - normalBatchControls.style.display = 'none'; - randomBatchControls.style.display = 'block'; - } else { - normalBatchControls.style.display = 'block'; - randomBatchControls.style.display = 'none'; - } - } - - const randomBatchMin = document.getElementById('randomBatchMin'); - if (randomBatchMin) randomBatchMin.value = state.randomBatchMin; - - const randomBatchMax = document.getElementById('randomBatchMax'); - if (randomBatchMax) randomBatchMax.value = state.randomBatchMax; - - // AUTO_CAPTCHA_ENABLED is always true - no toggle to set - - const cooldownSlider = document.getElementById('cooldownSlider'); - const cooldownInput = document.getElementById('cooldownInput'); - const cooldownValue = document.getElementById('cooldownValue'); - if (cooldownSlider) cooldownSlider.value = state.cooldownChargeThreshold; - if (cooldownInput) cooldownInput.value = state.cooldownChargeThreshold; - if (cooldownValue) cooldownValue.textContent = `${Utils.t('charges')}`; - - const overlayOpacitySlider = document.getElementById('overlayOpacitySlider'); - if (overlayOpacitySlider) overlayOpacitySlider.value = state.overlayOpacity; - const overlayOpacityValue = document.getElementById('overlayOpacityValue'); - if (overlayOpacityValue) - overlayOpacityValue.textContent = `${Math.round(state.overlayOpacity * 100)}%`; - const enableBlueMarbleToggle = document.getElementById('enableBlueMarbleToggle'); - if (enableBlueMarbleToggle) enableBlueMarbleToggle.checked = state.blueMarbleEnabled; - - const tokenSourceSelect = document.getElementById('tokenSourceSelect'); - if (tokenSourceSelect) tokenSourceSelect.value = state.tokenSource; - - const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); - if (colorAlgorithmSelect) colorAlgorithmSelect.value = state.colorMatchingAlgorithm; - const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); - if (enableChromaPenaltyToggle) enableChromaPenaltyToggle.checked = state.enableChromaPenalty; - const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); - if (chromaPenaltyWeightSlider) chromaPenaltyWeightSlider.value = state.chromaPenaltyWeight; - const chromaWeightValue = document.getElementById('chromaWeightValue'); - if (chromaWeightValue) chromaWeightValue.textContent = state.chromaPenaltyWeight; - const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); - if (transparencyThresholdInput) - transparencyThresholdInput.value = state.customTransparencyThreshold; - const whiteThresholdInput = document.getElementById('whiteThresholdInput'); - if (whiteThresholdInput) whiteThresholdInput.value = state.customWhiteThreshold; - // Notifications UI - const notifEnabledToggle = document.getElementById('notifEnabledToggle'); - if (notifEnabledToggle) notifEnabledToggle.checked = state.notificationsEnabled; - const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); - if (notifOnChargesToggle) notifOnChargesToggle.checked = state.notifyOnChargesReached; - const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); - if (notifOnlyUnfocusedToggle) - notifOnlyUnfocusedToggle.checked = state.notifyOnlyWhenUnfocused; - const notifIntervalInput = document.getElementById('notifIntervalInput'); - if (notifIntervalInput) notifIntervalInput.value = state.notificationIntervalMinutes; - NotificationManager.resetEdgeTracking(); - } catch (e) { - console.warn('Could not load bot settings:', e); - } - } - - // Initialize Turnstile generator integration - console.log('🚀 WPlace Auto-Image with Turnstile Token Generator loaded'); - console.log('🔑 Turnstile token generator: ALWAYS ENABLED (Background mode)'); - console.log('🎯 Manual pixel captcha solving: Available as fallback/alternative'); - console.log('📱 Turnstile widgets: DISABLED - pure background token generation only!'); - - // Function to enable file operations after initial startup setup is complete - function enableFileOperations() { - state.initialSetupComplete = true; - - const loadBtn = document.querySelector('#loadBtn'); - const loadFromFileBtn = document.querySelector('#loadFromFileBtn'); - const uploadBtn = document.querySelector('#uploadBtn'); - - if (loadBtn) { - loadBtn.disabled = false; - loadBtn.title = ''; - // Add a subtle animation to indicate the button is now available - loadBtn.style.animation = 'pulse 0.6s ease-in-out'; - setTimeout(() => { - if (loadBtn) loadBtn.style.animation = ''; - }, 600); - console.log('✅ Load Progress button enabled after initial setup'); - } - - if (loadFromFileBtn) { - loadFromFileBtn.disabled = false; - loadFromFileBtn.title = ''; - // Add a subtle animation to indicate the button is now available - loadFromFileBtn.style.animation = 'pulse 0.6s ease-in-out'; - setTimeout(() => { - if (loadFromFileBtn) loadFromFileBtn.style.animation = ''; - }, 600); - console.log('✅ Load from File button enabled after initial setup'); - } - - if (uploadBtn) { - uploadBtn.disabled = false; - uploadBtn.title = ''; - // Add a subtle animation to indicate the button is now available - uploadBtn.style.animation = 'pulse 0.6s ease-in-out'; - setTimeout(() => { - if (uploadBtn) uploadBtn.style.animation = ''; - }, 600); - console.log('✅ Upload Image button enabled after initial setup'); - } - - // Show a notification that file operations are now available - Utils.showAlert(Utils.t('fileOperationsAvailable'), 'success'); - } - - // Optimized token initialization with better timing and error handling - async function initializeTokenGenerator() { - // Skip if already have valid token - if (isTokenValid()) { - console.log('✅ Valid token already available, skipping initialization'); - updateUI('tokenReady', 'success'); - enableFileOperations(); // Enable file operations since initial setup is complete - return; - } - - try { - console.log('🔧 Initializing Turnstile token generator...'); - updateUI('initializingToken', 'default'); - - console.log('Attempting to load Turnstile script...'); - await Utils.loadTurnstile(); - console.log('Turnstile script loaded. Attempting to generate token...'); - - const token = await handleCaptchaWithRetry(); - if (token) { - setTurnstileToken(token); - console.log('✅ Startup token generated successfully'); - updateUI('tokenReady', 'success'); - Utils.showAlert(Utils.t('tokenGeneratorReady'), 'success'); - enableFileOperations(); // Enable file operations since initial setup is complete - } else { - console.warn( - '⚠️ Startup token generation failed (no token received), will retry when needed' - ); - updateUI('tokenRetryLater', 'warning'); - // Still enable file operations even if initial token generation fails - // Users can load progress and use manual/hybrid modes - enableFileOperations(); - } - } catch (error) { - console.error('❌ Critical error during Turnstile initialization:', error); // More specific error - updateUI('tokenRetryLater', 'warning'); - // Still enable file operations even if initial setup fails - // Users can load progress and use manual/hybrid modes - enableFileOperations(); - // Don't show error alert for initialization failures, just log them - } - } - - // Load theme preference immediately on startup before creating UI - loadThemePreference(); - applyTheme(); - - createUI().then(() => { - // Generate token automatically after UI is ready - setTimeout(initializeTokenGenerator, 1000); - - // Attach advanced color matching listeners (resize dialog) - const advancedInit = () => { - const chromaSlider = document.getElementById('chromaPenaltyWeightSlider'); - const chromaValue = document.getElementById('chromaWeightValue'); - const resetBtn = document.getElementById('resetAdvancedColorBtn'); - const algoSelect = document.getElementById('colorAlgorithmSelect'); - const chromaToggle = document.getElementById('enableChromaPenaltyToggle'); - const transInput = document.getElementById('transparencyThresholdInput'); - const whiteInput = document.getElementById('whiteThresholdInput'); - const ditherToggle = document.getElementById('enableDitheringToggle'); - if (algoSelect) - algoSelect.addEventListener('change', (e) => { - state.colorMatchingAlgorithm = e.target.value; - saveBotSettings(); - _updateResizePreview(); - }); - if (chromaToggle) - chromaToggle.addEventListener('change', (e) => { - state.enableChromaPenalty = e.target.checked; - saveBotSettings(); - _updateResizePreview(); - }); - if (chromaSlider && chromaValue) - chromaSlider.addEventListener('input', (e) => { - state.chromaPenaltyWeight = parseFloat(e.target.value) || 0.15; - chromaValue.textContent = state.chromaPenaltyWeight.toFixed(2); - saveBotSettings(); - _updateResizePreview(); - }); - if (transInput) - transInput.addEventListener('change', (e) => { - const v = parseInt(e.target.value, 10); - if (!isNaN(v) && v >= 0 && v <= 255) { - state.customTransparencyThreshold = v; - CONFIG.TRANSPARENCY_THRESHOLD = v; - saveBotSettings(); - _updateResizePreview(); - } - }); - if (whiteInput) - whiteInput.addEventListener('change', (e) => { - const v = parseInt(e.target.value, 10); - if (!isNaN(v) && v >= 200 && v <= 255) { - state.customWhiteThreshold = v; - CONFIG.WHITE_THRESHOLD = v; - saveBotSettings(); - _updateResizePreview(); - } - }); - if (ditherToggle) - ditherToggle.addEventListener('change', (e) => { - state.ditheringEnabled = e.target.checked; - saveBotSettings(); - _updateResizePreview(); - }); - if (resetBtn) - resetBtn.addEventListener('click', () => { - state.colorMatchingAlgorithm = 'lab'; - state.enableChromaPenalty = true; - state.chromaPenaltyWeight = 0.15; - state.customTransparencyThreshold = CONFIG.TRANSPARENCY_THRESHOLD = 100; - state.customWhiteThreshold = CONFIG.WHITE_THRESHOLD = 250; - saveBotSettings(); - const a = document.getElementById('colorAlgorithmSelect'); - if (a) a.value = 'lab'; - const ct = document.getElementById('enableChromaPenaltyToggle'); - if (ct) ct.checked = true; - if (chromaSlider) chromaSlider.value = 0.15; - if (chromaValue) chromaValue.textContent = '0.15'; - if (transInput) transInput.value = 100; - if (whiteInput) whiteInput.value = 250; - _updateResizePreview(); - Utils.showAlert(Utils.t('advancedColorSettingsReset'), 'success'); - }); - }; - // Delay to ensure resize UI built - setTimeout(advancedInit, 500); - - // Add cleanup on page unload - window.addEventListener('beforeunload', () => { - Utils.cleanupTurnstile(); - }); - }); -})(); From f41d54f9293e90483f996b15e46aac64203ccc76 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:19:45 +0800 Subject: [PATCH 06/18] Delete Auto-Farm.js --- Auto-Farm.js | 650 --------------------------------------------------- 1 file changed, 650 deletions(-) delete mode 100644 Auto-Farm.js diff --git a/Auto-Farm.js b/Auto-Farm.js deleted file mode 100644 index 6199f25..0000000 --- a/Auto-Farm.js +++ /dev/null @@ -1,650 +0,0 @@ -// eslint-disable-next-line prettier/prettier -; (async () => { - const CONFIG = { - START_X: 742, - START_Y: 1148, - PIXELS_PER_LINE: 100, - DELAY: 1000, - THEME: { - primary: '#000000', - secondary: '#111111', - accent: '#222222', - text: '#ffffff', - highlight: '#775ce3', - success: '#00ff00', - error: '#ff0000', - }, - }; - - const state = { - running: false, - paintedCount: 0, - charges: { count: 0, max: 80, cooldownMs: 30000 }, - userInfo: null, - lastPixel: null, - minimized: false, - menuOpen: false, - language: 'en', - autoRefresh: true, - pausedForManual: false, - }; - - const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); - const waitForSelector = async (selector, interval = 200, timeout = 5000) => { - const start = Date.now(); - while (Date.now() - start < timeout) { - const el = document.querySelector(selector); - if (el) return el; - await sleep(interval); - } - return null; - }; - - const originalFetch = window.fetch; - let capturedCaptchaToken = null; - window.fetch = async (url, options = {}) => { - if (typeof url === 'string' && url.includes('https://backend.wplace.live/s0/pixel/')) { - try { - const payload = JSON.parse(options.body || '{}'); - if (payload.t) { - console.log('✅ CAPTCHA Token Captured:', payload.t); - capturedCaptchaToken = payload.t; - if (state.pausedForManual) { - state.pausedForManual = false; - state.running = true; - updateUI( - state.language === 'pt' ? '🚀 Pintura reiniciada!' : '🚀 Farm resumed!', - 'success' - ); - paintLoop(); - } - } - } catch (e) {} - } - return originalFetch(url, options); - }; - - const fetchAPI = async (url, options = {}) => { - try { - const res = await fetch(url, { - credentials: 'include', - ...options, - }); - return await res.json(); - } catch (e) { - return null; - } - }; - - const getRandomPosition = () => ({ - x: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE), - y: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE), - }); - - const paintPixel = async (x, y) => { - const randomColor = Math.floor(Math.random() * 31) + 1; - const url = `https://backend.wplace.live/s0/pixel/${CONFIG.START_X}/${CONFIG.START_Y}`; - const payload = JSON.stringify({ - coords: [x, y], - colors: [randomColor], - t: capturedCaptchaToken, - }); - try { - const res = await originalFetch(url, { - method: 'POST', - headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, - credentials: 'include', - body: payload, - }); - if (res.status === 403) { - console.error('❌ 403 Forbidden. CAPTCHA token might be invalid or expired.'); - capturedCaptchaToken = null; - stoppedForToken = true; - return 'token_error'; - } - const data = await res.json(); - return data; - } catch (e) { - return null; - } - }; - - const getCharge = async () => { - const data = await fetchAPI('https://backend.wplace.live/me'); - if (data) { - state.userInfo = data; - state.charges = { - count: Math.floor(data.charges.count), - max: Math.floor(data.charges.max), - cooldownMs: data.charges.cooldownMs, - }; - if (state.userInfo.level) { - state.userInfo.level = Math.floor(state.userInfo.level); - } - } - return state.charges; - }; - - const detectUserLocation = async () => { - try { - const response = await fetch('https://ipapi.co/json/'); - const data = await response.json(); - if (data.country === 'BR') { - state.language = 'pt'; - } else if (data.country === 'US') { - state.language = 'en'; - } else { - state.language = 'en'; - } - } catch { - state.language = 'en'; - } - }; - - const paintLoop = async () => { - while (state.running) { - const { count, cooldownMs } = state.charges; - - if (count < 1) { - updateUI( - state.language === 'pt' - ? `⌛ Sem cargas. Esperando ${Math.ceil(cooldownMs / 1000)}s...` - : `⌛ No charges. Waiting ${Math.ceil(cooldownMs / 1000)}s...`, - 'status' - ); - await sleep(cooldownMs); - await getCharge(); - continue; - } - - const randomPos = getRandomPosition(); - const paintResult = await paintPixel(randomPos.x, randomPos.y); - if (paintResult === 'token_error') { - if (state.autoRefresh) { - await getCharge(); - if (state.charges.count < 2) { - if (!state.pausedForManual) { - updateUI( - state.language === 'pt' - ? '⚡ Aguardando pelo menos 2 cargas para auto-refresh...' - : 'Waiting for at least 2 charges for auto-refresh...', - 'status' - ); - state.pausedForManual = true; - } - while (state.charges.count < 2) { - await sleep(60000); - await getCharge(); - updateStats(); - } - state.pausedForManual = false; - } - updateUI( - state.language === 'pt' - ? '❌ Token expirado. Aguardando elemento Paint...' - : '❌ CAPTCHA token expired. Waiting for Paint button...', - 'error' - ); - const mainPaintBtn = await waitForSelector( - 'button.btn.btn-primary.btn-lg, button.btn-primary.sm\\:btn-xl' - ); - if (mainPaintBtn) mainPaintBtn.click(); - await sleep(500); - updateUI( - state.language === 'pt' ? 'Selecionando transparente...' : 'Selecting transparent...', - 'status' - ); - const transBtn = await waitForSelector('button#color-0'); - if (transBtn) transBtn.click(); - await sleep(500); - const canvas = await waitForSelector('canvas'); - if (canvas) { - 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); - const moveEvt = new MouseEvent('mousemove', { - clientX: centerX, - clientY: centerY, - bubbles: true, - }); - canvas.dispatchEvent(moveEvt); - const keyDown = new KeyboardEvent('keydown', { - key: ' ', - code: 'Space', - bubbles: true, - }); - const keyUp = new KeyboardEvent('keyup', { key: ' ', code: 'Space', bubbles: true }); - canvas.dispatchEvent(keyDown); - canvas.dispatchEvent(keyUp); - } - await sleep(500); - updateUI( - state.language === 'pt' ? 'Confirmando pintura...' : 'Confirming paint...', - 'status' - ); - let confirmBtn = await 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; - } - confirmBtn?.click(); - } else { - // insufficient charges or auto-refresh disabled - if (state.autoRefresh && state.charges.count < 2) { - updateUI( - state.language === 'pt' - ? '⚡ Cargas insuficientes para auto-refresh. Por favor, clique manualmente.' - : 'Insufficient charges for auto-refresh. Please click manually.', - 'error' - ); - } - if (!state.pausedForManual) { - updateUI( - state.language === 'pt' - ? 'Auto-refresh desativado. Por favor, clique no botão pintura manualmente.' - : 'Auto-refresh disabled. Please click the Paint button manually.', - 'status' - ); - state.pausedForManual = true; - } - state.running = false; - return; - } - await sleep(1000); - continue; - } - - if (paintResult?.painted === 1) { - state.paintedCount++; - state.lastPixel = { - x: CONFIG.START_X + randomPos.x, - y: CONFIG.START_Y + randomPos.y, - time: new Date(), - }; - state.charges.count--; - - document.getElementById('paintEffect').style.animation = 'pulse 0.5s'; - setTimeout(() => { - document.getElementById('paintEffect').style.animation = ''; - }, 500); - - updateUI(state.language === 'pt' ? '✅ Pixel pintado!' : '✅ Pixel painted!', 'success'); - } else { - updateUI(state.language === 'pt' ? '❌ Falha ao pintar' : '❌ Failed to paint', 'error'); - } - - await sleep(CONFIG.DELAY); - updateStats(); - } - }; - - const createUI = () => { - if (state.menuOpen) return; - state.menuOpen = true; - - const fontAwesome = document.createElement('link'); - fontAwesome.rel = 'stylesheet'; - fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'; - document.head.appendChild(fontAwesome); - - const style = document.createElement('style'); - style.textContent = ` - @keyframes pulse { - 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); }n - 70% { box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); } - 100% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); } - } - @keyframes slide-in { - from { transform: translateY(20px); opacity: 0; } - to { transform: translateY(0); opacity: 1; } - } - .wplace-bot-panel { - position: fixed; - top: 20px; - right: 20px; - width: 250px; - background: ${CONFIG.THEME.primary}; - border: 1px solid ${CONFIG.THEME.accent}; - border-radius: 8px; - padding: 0; - box-shadow: 0 5px 15px rgba(0,0,0,0.5); - z-index: 9999; - font-family: 'Segoe UI', Roboto, sans-serif; - color: ${CONFIG.THEME.text}; - animation: slide-in 0.4s ease-out; - overflow: hidden; - } - .wplace-header { - padding: 12px 15px; - background: ${CONFIG.THEME.secondary}; - color: ${CONFIG.THEME.highlight}; - font-size: 16px; - font-weight: 600; - display: flex; - justify-content: space-between; - align-items: center; - cursor: move; - user-select: none; - } - .wplace-header-title { - display: flex; - align-items: center; - gap: 8px; - } - .wplace-header-controls { - display: flex; - gap: 10px; - } - .wplace-header-btn { - background: none; - border: none; - color: ${CONFIG.THEME.text}; - cursor: pointer; - opacity: 0.7; - transition: opacity 0.2s; - } - .wplace-header-btn:hover { - opacity: 1; - } - .wplace-content { - padding: 15px; - display: ${state.minimized ? 'none' : 'block'}; - } - .wplace-controls { - display: flex; - gap: 10px; - margin-bottom: 15px; - } - .wplace-btn { - flex: 1; - padding: 10px; - border: none; - border-radius: 6px; - font-weight: 600; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - transition: all 0.2s; - } - .wplace-btn:hover { - transform: translateY(-2px); - } - .wplace-btn-primary { - background: ${CONFIG.THEME.accent}; - color: white; - } - .wplace-btn-stop { - background: ${CONFIG.THEME.error}; - color: white; - } - .wplace-stats { - background: ${CONFIG.THEME.secondary}; - padding: 12px; - border-radius: 6px; - margin-bottom: 15px; - } - .wplace-stat-item { - display: flex; - justify-content: space-between; - padding: 6px 0; - font-size: 14px; - } - .wplace-stat-label { - display: flex; - align-items: center; - gap: 6px; - opacity: 0.8; - } - .wplace-status { - padding: 8px; - border-radius: 4px; - text-align: center; - font-size: 13px; - } - .status-default { - background: rgba(255,255,255,0.1); - } - .status-success { - background: rgba(0, 255, 0, 0.1); - color: ${CONFIG.THEME.success}; - } - .status-error { - background: rgba(255, 0, 0, 0.1); - color: ${CONFIG.THEME.error}; - } - #paintEffect { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - border-radius: 8px; - } - `; - document.head.appendChild(style); - - const translations = { - pt: { - title: 'WPlace Auto-Farm', - start: 'Iniciar', - stop: 'Parar', - ready: 'Pronto para começar', - user: 'Usuário', - pixels: 'Pixels', - charges: 'Cargas', - level: 'Level', - }, - en: { - title: 'WPlace Auto-Farm', - start: 'Start', - stop: 'Stop', - ready: 'Ready to start', - user: 'User', - pixels: 'Pixels', - charges: 'Charges', - level: 'Level', - }, - }; - - const t = translations[state.language] || translations.en; - - const panel = document.createElement('div'); - panel.className = 'wplace-bot-panel'; - panel.innerHTML = ` -
-
-
- - ${t.title} -
-
- -
-
-
-
- - -
- -
-
-
-
${state.language === 'pt' ? 'Carregando...' : 'Loading...'}
-
-
-
- -
- ${t.ready} -
-
- `; - - document.body.appendChild(panel); - - const header = panel.querySelector('.wplace-header'); - let pos1 = 0, - pos2 = 0, - pos3 = 0, - pos4 = 0; - - header.onmousedown = dragMouseDown; - - function dragMouseDown(e) { - if (e.target.closest('.wplace-header-btn')) return; - - e = e || window.event; - e.preventDefault(); - pos3 = e.clientX; - pos4 = e.clientY; - document.onmouseup = closeDragElement; - document.onmousemove = elementDrag; - } - - function elementDrag(e) { - e = e || window.event; - e.preventDefault(); - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - panel.style.top = panel.offsetTop - pos2 + 'px'; - panel.style.left = panel.offsetLeft - pos1 + 'px'; - } - - function closeDragElement() { - document.onmouseup = null; - document.onmousemove = null; - } - - const toggleBtn = panel.querySelector('#toggleBtn'); - const minimizeBtn = panel.querySelector('#minimizeBtn'); - const statusText = panel.querySelector('#statusText'); - const content = panel.querySelector('.wplace-content'); - const statsArea = panel.querySelector('#statsArea'); - - toggleBtn.addEventListener('click', () => { - state.running = !state.running; - - if (state.running && !capturedCaptchaToken) { - updateUI( - state.language === 'pt' - ? '❌ Token não capturado. Clique em qualquer pixel primeiro.' - : '❌ CAPTCHA token not captured. Please click any pixel manually first.', - 'error' - ); - state.running = false; - return; - } - - if (state.running) { - toggleBtn.innerHTML = ` ${t.stop}`; - toggleBtn.classList.remove('wplace-btn-primary'); - toggleBtn.classList.add('wplace-btn-stop'); - updateUI( - state.language === 'pt' ? '🚀 Pintura iniciada!' : '🚀 Painting started!', - 'success' - ); - paintLoop(); - } else { - toggleBtn.innerHTML = ` ${t.start}`; - toggleBtn.classList.add('wplace-btn-primary'); - toggleBtn.classList.remove('wplace-btn-stop'); - statsArea.innerHTML = ''; - updateUI(state.language === 'pt' ? '⏹️ Parado' : '⏹️ Stopped', 'default'); - } - }); - - minimizeBtn.addEventListener('click', () => { - state.minimized = !state.minimized; - content.style.display = state.minimized ? 'none' : 'block'; - minimizeBtn.innerHTML = ``; - }); - - const autoRefreshCheckbox = panel.querySelector('#autoRefreshCheckbox'); - autoRefreshCheckbox.addEventListener('change', () => { - state.autoRefresh = autoRefreshCheckbox.checked; - }); - - window.addEventListener('beforeunload', () => { - state.menuOpen = false; - }); - }; - - window.updateUI = (message, type = 'default') => { - const statusText = document.querySelector('#statusText'); - if (statusText) { - statusText.textContent = message; - statusText.className = `wplace-status status-${type}`; - statusText.style.animation = 'none'; - void statusText.offsetWidth; - statusText.style.animation = 'slide-in 0.3s ease-out'; - } - }; - - window.updateStats = async () => { - await getCharge(); - const statsArea = document.querySelector('#statsArea'); - if (statsArea) { - const t = { - pt: { - user: 'Usuário', - pixels: 'Pixels', - charges: 'Cargas', - level: 'Level', - }, - en: { - user: 'User', - pixels: 'Pixels', - charges: 'Charges', - level: 'Level', - }, - }[state.language] || { - user: 'User', - pixels: 'Pixels', - charges: 'Charges', - level: 'Level', - }; - - statsArea.innerHTML = ` -
-
${t.user}
-
${state.userInfo.name}
-
-
-
${t.pixels}
-
${state.paintedCount}
-
-
-
${t.charges}
-
${Math.floor(state.charges.count)}/${Math.floor(state.charges.max)}
-
-
-
${t.level}
-
${state.userInfo?.level || '0'}
-
- `; - } - }; - - await detectUserLocation(); - createUI(); - await getCharge(); - updateStats(); -})(); From d83bda9bfe75346de93f091e50164718a3b92700 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:19:56 +0800 Subject: [PATCH 07/18] Delete themes directory --- themes/classic-light.css | 598 -------------------------------- themes/classic.css | 725 --------------------------------------- themes/neon.css | 519 ---------------------------- 3 files changed, 1842 deletions(-) delete mode 100644 themes/classic-light.css delete mode 100644 themes/classic.css delete mode 100644 themes/neon.css diff --git a/themes/classic-light.css b/themes/classic-light.css deleted file mode 100644 index b8079fc..0000000 --- a/themes/classic-light.css +++ /dev/null @@ -1,598 +0,0 @@ -/* WPlace Auto-Image Bot - Classic Light Theme */ - -/* Clean, bright theme based on classic design with light backgrounds */ - -.wplace-theme-classic-light { - /* === CORE COLORS === */ - --wplace-primary: #fff; /* Clean white */ - --wplace-secondary: #f8f9fa; /* Light gray */ - --wplace-accent: #e9ecef; /* Lighter gray */ - --wplace-text: #212529; /* Dark text */ - --wplace-highlight: #6f42c1; /* Purple highlight */ - --wplace-success: #28a745; /* Bootstrap green */ - --wplace-error: #dc3545; /* Bootstrap red */ - --wplace-warning: #ffc107; /* Bootstrap yellow */ - - /* === UI PROPERTIES === */ - --wplace-radius: 8px; - --wplace-btn-radius: 12px; - --wplace-border-style: solid; - --wplace-border-width: 1px; - --wplace-border-color: #415a77; - --wplace-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%); - --wplace-backdrop: blur(10px); - --wplace-font: 'Segoe UI', roboto, sans-serif; - - /* === EXTENDED COLOR PALETTE === */ - - /* Icon colors */ - --wplace-icon-primary: #4facfe; /* Light blue */ - --wplace-icon-secondary: #00f2fe; /* Cyan */ - --wplace-icon-palette: #f093fb; /* Pink */ - - /* Additional UI colors */ - --wplace-danger: #dc3545; /* Bootstrap red */ - --wplace-danger-dark: #c82333; /* Darker red */ - --wplace-muted-text: rgb(33 37 41 / 70%); - --wplace-highlight-secondary: #d3a4ff; - - /* Text variants */ - --wplace-text-secondary: rgb(33 37 41 / 90%); - --wplace-text-muted: rgb(33 37 41 / 70%); - --wplace-text-dim: rgb(33 37 41 / 60%); - --wplace-text-faded: rgb(33 37 41 / 80%); - - /* Background variants */ - --wplace-bg-input: #f3f3f3; /* neutral-100 */ - --wplace-bg-subtle: #f3f3f3; /* neutral-100 — hover */ - --wplace-bg-faint: #e0e0e0; /* neutral-200 */ - --wplace-bg-ghost: #d1d1d1; /* neutral-300 */ - --wplace-bg-whisper: #e0e0e0; /* can be used as whisper on hover */ - - /* Border variants */ - --wplace-border-subtle: #d1d1d1; /* neutral-300 */ - --wplace-border-faint: #e0e0e0; /* neutral-200 */ - --wplace-border-ghost: #e0e0e0; /* neutral-200 */ - --wplace-border-ultra-faint: #e0e0e0; /* neutral-200 — very faint */ - - /* Shadow variants */ - --wplace-shadow-drag: 0 12px 40px rgb(0 0 0 / 20%), 0 0 0 2px rgb(111 66 193 / 30%); - --wplace-shadow-notification: 0 4px 12px rgb(0 0 0 / 15%); - --wplace-shadow-slider-thumb: 0 3px 6px rgb(0 0 0 / 20%), 0 0 0 2px var(--wplace-icon-primary); - --wplace-shadow-slider-hover: 0 4px 8px rgb(0 0 0 / 25%), 0 0 0 3px var(--wplace-icon-primary); - - /* Animation colors */ - --wplace-pulse-start: rgb(40 167 69 / 70%); - --wplace-pulse-mid: rgb(40 167 69 / 0%); - --wplace-pulse-end: rgb(40 167 69 / 0%); - - /* Slider colors */ - --wplace-slider-thumb-bg: #6f42c1; - --wplace-slider-track-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); -} - -/* === COMPONENT STYLING === */ - -/* Main container with clean light styling */ -.wplace-theme-classic-light #wplace-image-bot-container { - background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; - color: var(--wplace-text) !important; - border: 1px solid rgb(0 0 0 / 15%) !important; - border-radius: 12px !important; - box-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - backdrop-filter: blur(10px) !important; -} - - -/* Stats container with proper contrast */ -.wplace-theme-classic-light #wplace-stats-container { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 12px !important; - box-shadow: 0 8px 32px rgb(0 0 0 / 15%) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - color: var(--wplace-text) !important; - position: fixed !important; - overflow: hidden !important; - z-index: 9998 !important; -} - -/* Headers with light gradient */ -.wplace-theme-classic-light .wplace-header { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; - color: var(--wplace-highlight) !important; - border-bottom: 1px solid rgb(0 0 0 / 15%) !important; - text-shadow: none !important; -} - -/* Stats header styling */ -.wplace-theme-classic-light #wplace-stats-container .wplace-header { - background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; - color: var(--wplace-text) !important; - border-bottom: 1px solid rgb(0 0 0 / 20%) !important; - text-shadow: none !important; - font-weight: 600 !important; -} - -/* Stats title specific styling */ -.wplace-theme-classic-light #wplace-stats-container .wplace-header .wplace-stats-title { - color: var(--wplace-text) !important; - text-shadow: none !important; -} - -/* Comprehensive text and element styling for light theme - scoped to bot containers only */ - -.wplace-theme-classic-light .wplace-status, -.wplace-theme-classic-light .wplace-stats, -.wplace-theme-classic-light .wplace-section, -.wplace-theme-classic-light .wplace-controls, -.wplace-theme-classic-light .wplace-data-management, -.wplace-theme-classic-light .wplace-cooldown-settings { - color: var(--wplace-text) !important; -} - -/* Buttons with light styling */ -.wplace-theme-classic-light .wplace-btn { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 12px !important; - color: var(--wplace-text) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - font-weight: 500 !important; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; -} - -/* Data management section buttons */ -.wplace-theme-classic-light .wplace-data-management .wplace-btn { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - color: #212529 !important; - font-weight: 500 !important; -} - -.wplace-theme-classic-light .wplace-btn:hover:not(:disabled) { - background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; - box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important; - transform: translateY(-1px) !important; -} - -.wplace-theme-classic-light .wplace-data-management .wplace-btn:hover:not(:disabled) { - background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; - box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important; -} - -/* Settings dialog */ -.wplace-theme-classic-light #wplace-settings-container { - background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; - border: 1px solid rgb(0 0 0 / 15%) !important; - border-radius: 12px !important; - box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - backdrop-filter: blur(10px) !important; - color: var(--wplace-text) !important; -} - -/* Settings dialog text elements */ -.wplace-theme-classic-light .wplace-stat-colors-grid * { - color: var(--wplace-text) !important; - font-weight: 500 !important; -} - -.wplace-theme-classic-light #wplace-settings-container * { - color: var(--wplace-text) !important; -} - -.wplace-theme-classic-light #wplace-settings-container .wplace-settings-section-wrapper * { - color: var(--wplace-text) !important; -} - -/* Color palette text labels */ -.wplace-theme-classic-light .wplace-color-label { - color: var(--wplace-text) !important; - font-weight: 500 !important; - text-shadow: none !important; -} - -.wplace-theme-classic-light .wplace-color-name { - color: var(--wplace-text) !important; - font-weight: 500 !important; -} - -.wplace-theme-classic-light .wplace-color-item-name { - color: #000 !important; - font-weight: 600 !important; - text-shadow: none !important; -} - -/* Resize dialog */ -.wplace-theme-classic-light .resize-container { - background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; - border: 1px solid rgb(0 0 0 / 15%) !important; - border-radius: 12px !important; - box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - backdrop-filter: blur(10px) !important; -} - -/* Settings header */ -.wplace-theme-classic-light .wplace-settings-header { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; - border-bottom: 1px solid rgb(0 0 0 / 15%) !important; -} - -.wplace-theme-classic-light .wplace-settings-title { - color: var(--wplace-text) !important; - text-shadow: none !important; -} - -.wplace-theme-classic-light .wplace-settings-close-btn { - background: rgb(0 0 0 / 5%) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 50% !important; - color: var(--wplace-text) !important; - transition: all 0.3s ease !important; -} - -.wplace-theme-classic-light .wplace-settings-close-btn:hover { - background: rgb(220 53 69 / 10%) !important; - border-color: var(--wplace-error) !important; - box-shadow: 0 0 12px rgb(220 53 69 / 30%) !important; -} - -/* Section titles */ -.wplace-theme-classic-light .wplace-section-title { - color: var(--wplace-highlight) !important; - text-shadow: none !important; - font-weight: 600 !important; -} - -/* Button variants with classic light colors */ -.wplace-theme-classic-light .wplace-btn-start { - background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important; - color: white !important; - font-weight: 600 !important; -} - -.wplace-theme-classic-light .wplace-btn-start:hover:not(:disabled) { - box-shadow: 0 4px 12px rgb(40 167 69 / 40%) !important; -} - -.wplace-theme-classic-light .wplace-btn-stop { - background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important; - color: white !important; - font-weight: 600 !important; -} - -.wplace-theme-classic-light .wplace-btn-stop:hover:not(:disabled) { - box-shadow: 0 4px 12px rgb(220 53 69 / 40%) !important; -} - -.wplace-theme-classic-light .wplace-btn-upload { - background: rgb(111 66 193 / 10%) !important; - border: 2px dashed var(--wplace-highlight) !important; - color: var(--wplace-highlight) !important; -} - -.wplace-theme-classic-light .wplace-btn-upload:hover:not(:disabled) { - background: rgb(111 66 193 / 15%) !important; - box-shadow: 0 4px 12px rgb(111 66 193 / 20%) !important; -} - -/* Progress bars with clean light styling */ -.wplace-theme-classic-light .wplace-progress { - background: rgb(0 0 0 / 10%) !important; - border: 1px solid rgb(0 0 0 / 15%) !important; - border-radius: 12px !important; -} - -.wplace-theme-classic-light .wplace-progress-bar { - background: linear-gradient(135deg, #6f42c1 0%, #9370db 100%) !important; - box-shadow: none !important; -} - -.wplace-theme-classic-light .wplace-progress-bar::after { - background: linear-gradient(90deg, transparent, rgb(255 255 255 / 30%), transparent) !important; -} - -/* Status indicators with light colors */ -.wplace-theme-classic-light .status-success { - background: rgb(40 167 69 / 10%) !important; - border-color: var(--wplace-success) !important; - color: var(--wplace-success) !important; - box-shadow: 0 0 15px rgb(40 167 69 / 20%) !important; - text-shadow: none !important; -} - -.wplace-theme-classic-light .status-error { - background: rgb(220 53 69 / 10%) !important; - border-color: var(--wplace-error) !important; - color: var(--wplace-error) !important; - box-shadow: 0 0 15px rgb(220 53 69 / 20%) !important; - text-shadow: none !important; -} - -.wplace-theme-classic-light .status-default { - background: rgb(111 66 193 / 10%) !important; - border-color: var(--wplace-highlight) !important; - color: var(--wplace-highlight) !important; - text-shadow: none !important; -} - -.wplace-theme-classic-light .wplace-stat-label { - color: var(--wplace-text) !important; - text-shadow: none !important; - font-weight: 500 !important; -} - -.wplace-theme-classic-light .wplace-stat-value { - color: var(--wplace-highlight) !important; - text-shadow: none !important; - font-weight: 600 !important; -} - -/* Sections with light styling */ -.wplace-theme-classic-light .wplace-section { - background: rgb(0 0 0 / 3%) !important; - border: 1px solid rgb(0 0 0 / 10%) !important; - border-radius: 12px !important; -} - -.wplace-theme-classic-light .wplace-status-section { - background: rgb(0 0 0 / 3%) !important; - border: 1px solid rgb(0 0 0 / 10%) !important; - border-radius: 12px !important; -} - -.wplace-theme-classic-light .wplace-settings-section-wrapper { - background: rgb(0 0 0 / 3%) !important; - border: 1px solid rgb(0 0 0 / 10%) !important; - border-radius: 12px !important; -} - -/* Form controls with light styling */ -.wplace-theme-classic-light .wplace-settings-select { - background: #fff !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 8px !important; - color: var(--wplace-text) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important; -} - -.wplace-theme-classic-light .wplace-settings-select:focus { - border-color: var(--wplace-highlight) !important; - box-shadow: 0 0 0 2px rgb(111 66 193 / 30%) !important; -} - -/* Dropdown menu options */ -.wplace-theme-classic-light .wplace-settings-select option { - background: #fff !important; - color: var(--wplace-text) !important; -} - -.wplace-theme-classic-light .wplace-settings-option { - background: #fff !important; - color: var(--wplace-text) !important; -} - -/* Sliders with classic gradient */ -.wplace-theme-classic-light .wplace-speed-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; - border-radius: 4px !important; -} - -.wplace-theme-classic-light .wplace-overlay-opacity-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; - border-radius: 4px !important; -} - -.wplace-theme-classic-light .wplace-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; - border-radius: 4px !important; -} - -.wplace-theme-classic-light .wplace-slider::-webkit-slider-thumb { - -webkit-appearance: none; - width: 16px; - height: 16px; - border-radius: 50%; - background: var(--wplace-slider-thumb-bg); - border: 1px solid rgb(0 0 0 / 20%); - cursor: pointer; - transition: all 0.2s ease; - box-shadow: 0 2px 4px rgb(0 0 0 / 20%); -} - -.wplace-theme-classic-light .wplace-speed-value { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important; - border-radius: 8px !important; - color: white !important; - font-weight: 600 !important; - box-shadow: 0 3px 10px rgb(79 172 254 / 30%) !important; - border: 1px solid rgb(0 0 0 / 10%) !important; -} - -/* Settings labels */ -.wplace-theme-classic-light .wplace-settings-section-label { - color: var(--wplace-highlight) !important; - text-shadow: none !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; - font-weight: 600 !important; -} - -/* Icon colors for classic light theme */ -.wplace-theme-classic-light .wplace-icon-key { color: #4facfe; } -.wplace-theme-classic-light .wplace-icon-robot { color: #4facfe; } -.wplace-theme-classic-light .wplace-icon-speed { color: #4facfe; } -.wplace-theme-classic-light .wplace-icon-bell { color: #ffc107; } -.wplace-theme-classic-light .wplace-icon-palette { color: #f093fb; } -.wplace-theme-classic-light .wplace-icon-globe { color: #ffeaa7; } -.wplace-theme-classic-light .wplace-icon-paint { color: #4facfe; } -.wplace-theme-classic-light .wplace-icon-eye { color: #6f42c1; } - -/* Clean light theme animations */ -@keyframes light-shimmer { - 0% { - transform: translateX(-100%); - } - - 100% { - transform: translateX(200%); - } -} - -/* Turnstile/CF checkbox overlay - CRITICAL FIX */ -.wplace-theme-classic-light .wplace-turnstile-overlay { - background: rgb(255 255 255 / 98%) !important; - border-radius: 12px !important; - box-shadow: 0 8px 32px rgb(0 0 0 / 30%) !important; - backdrop-filter: blur(10px) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - color: var(--wplace-text) !important; - font-family: 'Segoe UI', Roboto, sans-serif !important; -} - -.wplace-theme-classic-light .wplace-turnstile-title { - color: var(--wplace-text) !important; -} - -.wplace-theme-classic-light .wplace-turnstile-hide-btn { - color: var(--wplace-text) !important; - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 6px !important; - background: rgb(0 0 0 / 5%) !important; -} - -.wplace-theme-classic-light .wplace-turnstile-hide-btn:hover { - background: rgb(0 0 0 / 10%) !important; -} - -/* Dual control layout styling for classic light theme */ -.wplace-theme-classic-light .wplace-dual-control-compact { - display: flex; - flex-direction: row; - align-items: center; - gap: 12px; - margin: 8px 0; - flex-wrap: wrap; -} - -.wplace-theme-classic-light .wplace-slider-container-compact { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 160px; -} - -.wplace-theme-classic-light .wplace-speed-slider-container-compact { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 160px; -} - -.wplace-theme-classic-light .wplace-input-group-compact { - display: flex; - align-items: center; - gap: 4px; - flex-shrink: 0; -} - -.wplace-theme-classic-light .wplace-input-btn-compact { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; - border: none; - border-radius: 4px; - width: 22px; - height: 22px; - cursor: pointer; - font-weight: bold; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -.wplace-theme-classic-light .wplace-input-btn-compact:hover { - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0,0,0,0.3); - background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%); -} - -.wplace-theme-classic-light .wplace-input-btn-compact:active { - transform: translateY(0); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -.wplace-theme-classic-light .wplace-number-input-compact { - background: #fff; - border: 1px solid rgb(0 0 0 / 20%); - border-radius: 4px; - color: var(--wplace-text); - padding: 4px 8px; - font-size: 12px; - width: 50px; - text-align: center; - transition: all 0.2s ease; - height: 22px; -} - -.wplace-theme-classic-light .wplace-number-input-compact:focus { - outline: none; - border-color: #4facfe; - box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3); - background: #f8f9fa; -} - -.wplace-theme-classic-light .wplace-input-label-compact { - color: var(--wplace-text); - font-size: 11px; - margin-left: 4px; - white-space: nowrap; -} - -.wplace-theme-classic-light .wplace-batch-size-header { - margin-bottom: 8px; -} - -.wplace-theme-classic-light .wplace-batch-size-label { - color: var(--wplace-highlight); - font-weight: 500; - font-size: 13px; -} - -/* Cooldown control styling for classic light theme */ -.wplace-theme-classic-light .wplace-cooldown-control { - margin: 12px 0; -} - -.wplace-theme-classic-light .wplace-cooldown-control label { - color: var(--wplace-highlight); - font-weight: 500; - font-size: 13px; - margin-bottom: 8px; - display: block; -} - -/* Color swatches with light styling */ -.wplace-theme-classic-light .wplace-color-swatch { - border: 1px solid rgb(0 0 0 / 20%) !important; - border-radius: 4px !important; - box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important; -} - -.wplace-theme-classic-light .wplace-color-swatch:hover { - box-shadow: 0 4px 16px rgb(111 66 193 / 30%) !important; - transform: translateY(-1px); -} - -.wplace-theme-classic-light .wplace-stat-colors-grid { - background: rgb(0 0 0 / 5%) !important; - border-radius: 8px !important; - border: 1px solid rgb(0 0 0 / 10%) !important; -} diff --git a/themes/classic.css b/themes/classic.css deleted file mode 100644 index 857ce6e..0000000 --- a/themes/classic.css +++ /dev/null @@ -1,725 +0,0 @@ -/* WPlace Auto-Image Bot - Classic Theme */ -/* Clean, modern UI with gradients and subtle effects */ - -.wplace-theme-classic { - --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-success: #0f0; - --wplace-error: #f00; - --wplace-warning: #fa0; - --wplace-radius: 12px; - --wplace-btn-radius: 16px; - --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; - --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; - --wplace-highlight-secondary: #d3a4ff; - - /* Slider colors */ - --wplace-slider-track-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); -} - -/* Classic theme container styling */ -:root #wplace-image-bot-container, -.wplace-theme-classic #wplace-image-bot-container { - 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); -} - -:root #wplace-stats-container, -.wplace-theme-classic #wplace-stats-container { - 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); -} - -:root #wplace-settings-container, -.wplace-theme-classic #wplace-settings-container { - 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); -} - -:root .wplace-header, -.wplace-theme-classic .wplace-header { - background: var(--wplace-secondary); - color: var(--wplace-highlight); - text-shadow: 0 1px 2px rgb(0 0 0 / 50%); -} - -:root .wplace-section-title, -.wplace-theme-classic .wplace-section-title { - color: var(--wplace-highlight); -} - -:root .wplace-stat-value, -.wplace-theme-classic .wplace-stat-value { - color: var(--wplace-highlight); -} - -:root .wplace-status-section, -.wplace-theme-classic .wplace-status-section { - border-radius: var(--wplace-radius); - background: rgb(255 255 255 / 3%); - border: 1px solid rgb(255 255 255 / 10%); -} - -:root .wplace-section, -.wplace-theme-classic .wplace-section { - border-radius: var(--wplace-radius); - background: rgb(255 255 255 / 3%); -} - -:root .wplace-stats, -.wplace-theme-classic .wplace-stats { - border-radius: var(--wplace-radius); - background: rgb(255 255 255 / 3%); - border: 1px solid rgb(255 255 255 / 10%); -} - -:root .wplace-status, -.wplace-theme-classic .wplace-status { - border-radius: var(--wplace-radius); -} - -:root .wplace-alert-base, -.wplace-theme-classic .wplace-alert-base { - border-radius: var(--wplace-radius); - font-family: var(--wplace-font); -} - -:root .wplace-settings-container-base, -.wplace-theme-classic .wplace-settings-container-base { - font-family: var(--wplace-font); - border-radius: var(--wplace-radius); - background: var(--wplace-primary); - color: var(--wplace-text); - box-shadow: var(--wplace-shadow); - backdrop-filter: var(--wplace-backdrop); -} - -/* Button styling for classic theme */ -:root .wplace-btn, -.wplace-theme-classic .wplace-btn { - background: linear-gradient(135deg, #222 0%, #4a4a4a 100%); - border: 1px solid rgb(255 255 255 / 10%); - color: var(--wplace-text); - border-radius: var(--wplace-btn-radius); - font-family: var(--wplace-font); -} - -:root .wplace-btn::before, -.wplace-theme-classic .wplace-btn::before { - background: linear-gradient(90deg, transparent, rgb(255 255 255 / 10%), transparent); -} - -:root .wplace-btn:disabled, -.wplace-theme-classic .wplace-btn:disabled { - box-shadow: none !important; -} - -:root .wplace-btn:hover:not(:disabled), -.wplace-theme-classic .wplace-btn:hover:not(:disabled) { - box-shadow: 0 4px 12px rgb(0 0 0 / 40%); -} - -:root .wplace-btn-overlay.active, -.wplace-theme-classic .wplace-btn-overlay.active { - background: linear-gradient(135deg, #29b6f6 0%, #8e2de2 100%); - box-shadow: 0 0 15px #8e2de2; -} - -/* Button variants - exact upstream main colors */ -:root .wplace-btn-primary, -.wplace-theme-classic .wplace-btn-primary { - background: linear-gradient(135deg, #222 0%, #6a5acd 100%); - color: var(--wplace-text); -} - -:root .wplace-btn-upload, -.wplace-theme-classic .wplace-btn-upload { - background: linear-gradient(135deg, #111 0%, #4a4a4a 100%); - color: var(--wplace-text); - border: 1px dashed var(--wplace-highlight) !important; -} - -:root .wplace-btn-start, -.wplace-theme-classic .wplace-btn-start { - background: linear-gradient(135deg, var(--wplace-success) 0%, #228b22 100%); - color: white; -} - -:root .wplace-btn-stop, -.wplace-theme-classic .wplace-btn-stop { - background: linear-gradient(135deg, var(--wplace-error) 0%, #dc143c 100%); - color: white; -} - -:root .wplace-btn-select, -.wplace-theme-classic .wplace-btn-select { - background: linear-gradient(135deg, var(--wplace-highlight) 0%, #9370db 100%); - color: white; -} - -:root .wplace-btn-file, -.wplace-theme-classic .wplace-btn-file { - background: linear-gradient(135deg, #ff8c00 0%, #ff7f50 100%); - color: white; -} - -:root .wplace-btn.active, -:root .wplace-btn[aria-pressed="true"], -.wplace-theme-classic .wplace-btn.active, -.wplace-theme-classic .wplace-btn[aria-pressed="true"] { - background: var(--wplace-highlight) !important; - color: #000 !important; - border-color: var(--wplace-text) !important; - box-shadow: 0 0 8px rgb(0 0 0 / 25%) inset, 0 0 6px rgb(0 0 0 / 20%) !important; -} - -:root .wplace-btn.active i, -:root .wplace-btn[aria-pressed="true"] i, -.wplace-theme-classic .wplace-btn.active i, -.wplace-theme-classic .wplace-btn[aria-pressed="true"] i { - filter: drop-shadow(0 0 3px #000); -} - -:root .mask-mode-group .wplace-btn.active, -:root .mask-mode-group .wplace-btn[aria-pressed="true"], -.wplace-theme-classic .mask-mode-group .wplace-btn.active, -.wplace-theme-classic .mask-mode-group .wplace-btn[aria-pressed="true"] { - background: var(--wplace-highlight); - color: #000; - border-color: var(--wplace-text); - box-shadow: 0 0 8px rgb(0 0 0 / 25%) inset, 0 0 6px rgb(0 0 0 / 20%); -} - -/* Status styling for classic theme */ -:root .status-default, -.wplace-theme-classic .status-default { - background: rgb(255 255 255 / 10%); - border-color: var(--wplace-text); - color: var(--wplace-text); -} - -:root .status-success, -.wplace-theme-classic .status-success { - background: rgb(0 255 0 / 10%); - border-color: var(--wplace-success); - color: var(--wplace-success); - box-shadow: 0 0 15px var(--wplace-success); -} - -:root .status-error, -.wplace-theme-classic .status-error { - background: rgb(255 0 0 / 10%); - border-color: var(--wplace-error); - color: var(--wplace-error); - box-shadow: 0 0 15px var(--wplace-error); -} - -:root .status-warning, -.wplace-theme-classic .status-warning { - background: rgb(255 165 0 / 10%); - border-color: var(--wplace-warning); - color: var(--wplace-warning); - box-shadow: 0 0 15px var(--wplace-warning); -} - -:root .wplace-status.status-default, -.wplace-theme-classic .wplace-status.status-default { - color: var(--wplace-text); - background: rgb(255 255 255 / 10%); - border-color: var(--wplace-text); -} - -/* Alert styling for classic theme */ -:root .wplace-alert-info, -.wplace-theme-classic .wplace-alert-info { - background: linear-gradient(135deg, #3498db, #2980b9); - box-shadow: 0 4px 12px rgb(52 152 219 / 30%); -} - -:root .wplace-alert-success, -.wplace-theme-classic .wplace-alert-success { - background: linear-gradient(135deg, #27ae60, #229954); - box-shadow: 0 4px 12px rgb(39 174 96 / 30%); -} - -:root .wplace-alert-warning, -.wplace-theme-classic .wplace-alert-warning { - background: linear-gradient(135deg, #f39c12, #e67e22); - box-shadow: 0 4px 12px rgb(243 156 18 / 30%); -} - -:root .wplace-alert-error, -.wplace-theme-classic .wplace-alert-error { - background: linear-gradient(135deg, #e74c3c, #c0392b); - box-shadow: 0 4px 12px rgb(231 76 60 / 30%); -} - -/* Progress bar styling for classic theme */ -:root .wplace-progress, -.wplace-theme-classic .wplace-progress { - background: rgb(0 0 0 / 30%); - border: 1px solid rgb(255 255 255 / 10%); - border-radius: var(--wplace-radius); -} - -:root .wplace-progress-bar, -.wplace-theme-classic .wplace-progress-bar { - background: linear-gradient(135deg, var(--wplace-highlight) 0%, #9370db 100%); -} - -:root .wplace-progress-bar::after, -.wplace-theme-classic .wplace-progress-bar::after { - background: linear-gradient(90deg, transparent, rgb(255 255 255 / 30%), transparent); -} - -/* Header and sections styling for classic theme */ -:root .wplace-header-btn, -.wplace-theme-classic .wplace-header-btn { - background: rgb(255 255 255 / 10%); - color: var(--wplace-highlight); - border-radius: 4px; - font-family: var(--wplace-font); -} - -:root .wplace-header-btn:hover, -.wplace-theme-classic .wplace-header-btn:hover { - background: #222; - color: var(--wplace-text); -} - -/* Font and typography for classic theme */ -:root .wplace-turnstile-overlay, -.wplace-theme-classic .wplace-turnstile-overlay { - background: rgb(0 0 0 / 90%) !important; - border-radius: var(--wplace-radius) !important; - box-shadow: 0 8px 32px rgb(0 0 0 / 40%) !important; - backdrop-filter: var(--wplace-backdrop) !important; - border: 1px solid rgb(255 255 255 / 20%) !important; - color: var(--wplace-text) !important; - font-family: var(--wplace-font) !important; -} - -/* Auto light/dark support for classic theme */ -@media (prefers-color-scheme: light) { - :root .theme-auto, - .wplace-theme-classic .theme-auto { - --wplace-primary: #fff; - --wplace-secondary: #f5f5f5; - --wplace-accent: #007acc; - --wplace-text: #333; - } -} - -@media (prefers-color-scheme: dark) { - :root .theme-auto, - .wplace-theme-classic .theme-auto { - --wplace-primary: #1e1e1e; - --wplace-secondary: #2d2d30; - --wplace-accent: var(--wplace-highlight); - --wplace-text: #fff; - } -} - -/* Border and color styling for classic theme */ -:root .wplace-color-item-name, -.wplace-theme-classic .wplace-color-item-name { - color: #ccc; -} - -:root .wplace-color-swatch, -.wplace-theme-classic .wplace-color-swatch { - border: 1px solid rgb(255 255 255 / 20%); - border-radius: 4px; -} - -:root .wplace-stat-color-swatch, -.wplace-theme-classic .wplace-stat-color-swatch { - border-radius: 3px; - border: 1px solid rgb(255 255 255 / 10%); - box-shadow: inset 0 0 2px rgb(0 0 0 / 50%); -} - -:root .resize-tools button, -.wplace-theme-classic .resize-tools button { - border-radius: 6px; - border: 1px solid rgb(255 255 255 / 20%); - background: rgb(255 255 255 / 6%); - color: #fff; -} - -:root .resize-slider, -.wplace-theme-classic .resize-slider { - background: #ccc; - border-radius: var(--wplace-radius); -} - -/* Text effects and filters for classic theme */ -:root .wplace-color-swatch:not(.active), -.wplace-theme-classic .wplace-color-swatch:not(.active) { - filter: grayscale(80%); -} - -:root .wplace-color-swatch.unavailable, -.wplace-theme-classic .wplace-color-swatch.unavailable { - border-color: #666; -} - -:root .wplace-color-swatch.unavailable:not(.active), -.wplace-theme-classic .wplace-color-swatch.unavailable:not(.active) { - filter: grayscale(90%); -} - -:root .wplace-color-swatch.active::after, -.wplace-theme-classic .wplace-color-swatch.active::after { - text-shadow: 0 0 3px black; -} - -/* Icon colors for classic theme */ -:root .wplace-icon-key, -.wplace-theme-classic .wplace-icon-key { - color: #4facfe; -} - -:root .wplace-icon-robot, -.wplace-theme-classic .wplace-icon-robot { - color: #4facfe; -} - -:root .wplace-icon-speed, -.wplace-theme-classic .wplace-icon-speed { - color: #4facfe; -} - -:root .wplace-icon-bell, -.wplace-theme-classic .wplace-icon-bell { - color: #ffd166; -} - -:root .wplace-icon-palette, -.wplace-theme-classic .wplace-icon-palette { - color: #f093fb; -} - -:root .wplace-icon-globe, -.wplace-theme-classic .wplace-icon-globe { - color: #ffeaa7; -} - -:root .wplace-icon-paint, -.wplace-theme-classic .wplace-icon-paint { - color: #4facfe; -} - -:root .wplace-icon-eye, -.wplace-theme-classic .wplace-icon-eye { - color: var(--wplace-highlight); -} - -/* Form controls and sliders for classic theme */ -:root .wplace-slider, -.wplace-theme-classic .wplace-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); - border-radius: 4px; -} - -:root .wplace-slider::-webkit-slider-thumb, -.wplace-theme-classic .wplace-slider::-webkit-slider-thumb { - -webkit-appearance: none; - width: 16px; - height: 16px; - border-radius: 50%; - background: var(--wplace-slider-thumb-bg); - border: none; - cursor: pointer; - transition: all 0.2s ease; -} - -:root .wplace-speed-slider, -.wplace-theme-classic .wplace-speed-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); - border-radius: 4px; -} - -:root .wplace-speed-slider::-webkit-slider-thumb, -.wplace-theme-classic .wplace-speed-slider::-webkit-slider-thumb { - border-radius: 50%; - border: 2px solid; -} - -:root .wplace-speed-slider::-moz-range-thumb, -.wplace-theme-classic .wplace-speed-slider::-moz-range-thumb { - border-radius: 50%; - border: 2px solid; -} - -:root .wplace-turnstile-title, -.wplace-theme-classic .wplace-turnstile-title { - color: var(--wplace-text) !important; -} - -:root .wplace-turnstile-hide-btn, -.wplace-theme-classic .wplace-turnstile-hide-btn { - color: var(--wplace-text) !important; - border: 1px solid rgb(255 255 255 / 20%) !important; - border-radius: 6px !important; -} - -:root .wplace-turnstile-hide-btn:hover, -.wplace-theme-classic .wplace-turnstile-hide-btn:hover { - background: rgb(255 255 255 / 10%) !important; -} - -:root .wplace-settings-select, -.wplace-theme-classic .wplace-settings-select { - background: rgb(255 255 255 / 15%); - color: white; - border: 1px solid rgb(255 255 255 / 20%); - border-radius: 8px; - box-shadow: 0 3px 10px rgb(0 0 0 / 10%); -} - -:root .wplace-settings-option, -.wplace-theme-classic .wplace-settings-option { - background: #2d3748; - color: white; -} - -:root .wplace-stat-colors-grid, -.wplace-theme-classic .wplace-stat-colors-grid { - background: rgb(0 0 0 / 20%); - border-radius: 4px; -} - -:root .resize-overlay, -.wplace-theme-classic .resize-overlay { - background: rgb(0 0 0 / 80%); -} - -:root .wplace-overlay, -.wplace-theme-classic .wplace-overlay { - background: rgb(0 0 0 / 80%); -} - -:root .wplace-settings-error, -.wplace-theme-classic .wplace-settings-error { - background: rgb(255 0 0 / 40%) !important; -} - -/* Settings sections and batch controls for classic theme */ -:root .wplace-settings-section-wrapper, -.wplace-theme-classic .wplace-settings-section-wrapper { - background: rgb(255 255 255 / 10%); - border-radius: var(--wplace-radius); - border: 1px solid rgb(255 255 255 / 10%); -} - -:root .wplace-batch-controls, -.wplace-theme-classic .wplace-batch-controls { - background: rgb(255 255 255 / 10%); - border-radius: var(--wplace-radius); - border: 1px solid rgb(255 255 255 / 10%); -} - -:root .wplace-overlay-opacity-slider, -.wplace-theme-classic .wplace-overlay-opacity-slider { - background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); - border-radius: 4px; -} - -:root .wplace-speed-value, -.wplace-theme-classic .wplace-speed-value { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - border-radius: 8px; - color: white; - box-shadow: 0 3px 10px rgb(79 172 254 / 30%); - border: 1px solid rgb(255 255 255 / 20%); -} - -/* Additional elements for classic theme */ -:root .wplace-color-divider, -.wplace-theme-classic .wplace-color-divider { - background: rgb(255 255 255 / 10%); -} - -:root .wplace-settings-select option, -.wplace-theme-classic .wplace-settings-select option { - background: #2d3748; - color: white; -} - -:root .wplace-settings-description, -.wplace-theme-classic .wplace-settings-description { - color: rgb(255 255 255 / 70%); -} - -:root .wplace-settings-apply-btn, -.wplace-theme-classic .wplace-settings-apply-btn { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; - border-radius: 8px; -} - -/* Dual control layout styling for classic theme */ -:root .wplace-dual-control-compact, -.wplace-theme-classic .wplace-dual-control-compact { - display: flex; - flex-direction: row; - align-items: center; - gap: 12px; - margin: 8px 0; - flex-wrap: wrap; -} - -:root .wplace-slider-container-compact, -.wplace-theme-classic .wplace-slider-container-compact { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 160px; -} - -:root .wplace-speed-slider-container-compact, -.wplace-theme-classic .wplace-speed-slider-container-compact { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 160px; -} - -:root .wplace-input-group-compact, -.wplace-theme-classic .wplace-input-group-compact { - display: flex; - align-items: center; - gap: 4px; - flex-shrink: 0; -} - -:root .wplace-input-btn-compact, -.wplace-theme-classic .wplace-input-btn-compact { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; - border: none; - border-radius: 4px; - width: 22px; - height: 22px; - cursor: pointer; - font-weight: bold; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -:root .wplace-input-btn-compact:hover, -.wplace-theme-classic .wplace-input-btn-compact:hover { - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0,0,0,0.3); - background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%); -} - -:root .wplace-input-btn-compact:active, -.wplace-theme-classic .wplace-input-btn-compact:active { - transform: translateY(0); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -:root .wplace-number-input-compact, -.wplace-theme-classic .wplace-number-input-compact { - background: rgba(255,255,255,0.1); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 4px; - color: white; - padding: 4px 8px; - font-size: 12px; - width: 50px; - text-align: center; - transition: all 0.2s ease; - height: 22px; -} - -:root .wplace-number-input-compact:focus, -.wplace-theme-classic .wplace-number-input-compact:focus { - outline: none; - border-color: #4facfe; - box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3); - background: rgba(255,255,255,0.15); -} - -:root .wplace-input-label-compact, -.wplace-theme-classic .wplace-input-label-compact { - color: rgba(255,255,255,0.8); - font-size: 11px; - margin-left: 4px; - white-space: nowrap; -} - -:root .wplace-batch-size-header, -.wplace-theme-classic .wplace-batch-size-header { - margin-bottom: 8px; -} - -:root .wplace-batch-size-label, -.wplace-theme-classic .wplace-batch-size-label { - color: var(--wplace-highlight); - font-weight: 500; - font-size: 13px; -} - -/* Cooldown control styling for classic theme */ -:root .wplace-cooldown-control, -.wplace-theme-classic .wplace-cooldown-control { - margin: 12px 0; -} - -:root .wplace-cooldown-control label, -.wplace-theme-classic .wplace-cooldown-control label { - color: var(--wplace-highlight); - font-weight: 500; - font-size: 13px; - margin-bottom: 8px; - display: block; -} - -/* Resize dialog styling for classic theme */ -:root .resize-container, -.wplace-theme-classic .resize-container { - background: #000; - border: 1px solid #fff; - border-radius: 12px; - box-shadow: 0 0 20px rgb(0 0 0 / 50%); -} diff --git a/themes/neon.css b/themes/neon.css deleted file mode 100644 index 1daa02b..0000000 --- a/themes/neon.css +++ /dev/null @@ -1,519 +0,0 @@ -/* WPlace Auto-Image Bot - Neon Theme */ -/* Retro cyberpunk aesthetic with green neon glow effects */ - -.wplace-theme-neon { - /* Neon theme colors - matching upstream main neon theme */ - --wplace-primary: #1a1a2e; - --wplace-secondary: #16213e; - --wplace-accent: #0f3460; - --wplace-text: #00ff41; - --wplace-highlight: #ff6b35; - --wplace-success: #39ff14; - --wplace-error: #ff073a; - --wplace-warning: #ff0; - - /* Neon UI properties */ - --wplace-radius: 0; - --wplace-border-style: solid; - --wplace-border-width: 2px; - --wplace-border-color: #00ff41; - --wplace-shadow: 0 0 20px rgb(0 255 65 / 30%), inset 0 0 20px rgb(0 255 65 / 10%); - --wplace-backdrop: none; - --wplace-font: 'Press Start 2P', monospace, 'Courier New'; - - /* Z-index layers */ - --wplace-z-overlay: 10000; - --wplace-z-alert: 10002; - --wplace-z-settings: 10002; - - /* Feature toggles */ - --wplace-scanline: 1; - --wplace-pixel-blink: 1; - - /* Icon colors - neon variants */ - --wplace-icon-primary: #00ff41; - --wplace-icon-secondary: #39ff14; - --wplace-icon-palette: #ff6b35; - - /* Additional UI colors - neon variants */ - --wplace-danger: #ff073a; - --wplace-danger-dark: #cc0531; - --wplace-muted-text: #00ff4180; - --wplace-highlight-secondary: #fa0; - - /* Text variants - neon style */ - --wplace-text-secondary: #00ff41dd; - --wplace-text-muted: #00ff41bb; - --wplace-text-dim: #00ff4199; - --wplace-text-faded: #00ff41cc; - - /* Background variants - neon style */ - --wplace-bg-input: rgb(0 255 65 / 15%); - --wplace-bg-subtle: rgb(0 255 65 / 8%); - --wplace-bg-faint: rgb(0 255 65 / 5%); - --wplace-bg-ghost: rgb(0 255 65 / 3%); - --wplace-bg-whisper: rgb(0 255 65 / 2%); - - /* Border variants - neon style */ - --wplace-border-subtle: rgb(0 255 65 / 40%); - --wplace-border-faint: rgb(0 255 65 / 25%); - --wplace-border-ghost: rgb(0 255 65 / 15%); - --wplace-border-ultra-faint: rgb(0 255 65 / 8%); - - /* Shadow variants - neon style */ - --wplace-shadow-drag: 0 12px 40px rgb(0 255 65 / 60%), 0 0 0 2px rgb(0 255 65 / 80%), 0 0 20px rgb(0 255 65 / 30%); - --wplace-shadow-notification: 0 4px 12px rgb(0 255 65 / 40%), 0 0 15px rgb(0 255 65 / 20%); - --wplace-shadow-slider-thumb: 0 3px 6px rgb(0 255 65 / 50%), 0 0 0 2px var(--wplace-icon-primary), 0 0 10px rgb(0 255 65 / 30%); - --wplace-shadow-slider-hover: 0 4px 8px rgb(0 255 65 / 60%), 0 0 0 3px var(--wplace-icon-primary), 0 0 15px rgb(0 255 65 / 40%); - - /* Animation colors - neon style */ - --wplace-pulse-start: rgb(0 255 65 / 80%); - --wplace-pulse-mid: rgb(0 255 65 / 0%); - --wplace-pulse-end: rgb(0 255 65 / 0%); - - /* Slider colors - neon style */ - --wplace-slider-thumb-bg: #00ff41; -} - -/* Neon-specific styling overrides */ -.wplace-theme-neon #wplace-image-bot-container { - background: var(--wplace-primary) !important; - border: 2px solid var(--wplace-text) !important; - border-radius: 0 !important; - box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important; - font-family: var(--wplace-font) !important; -} - -.wplace-theme-neon #wplace-stats-container { - background: var(--wplace-primary) !important; - border: 2px solid var(--wplace-text) !important; - border-radius: 0 !important; - box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important; - font-family: var(--wplace-font) !important; - position: fixed !important; - overflow: hidden !important; - z-index: 9998 !important; -} - -.wplace-theme-neon .wplace-header { - background: var(--wplace-secondary) !important; - border-bottom: 1px solid var(--wplace-text) !important; - color: var(--wplace-text) !important; - text-shadow: 0 0 10px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; -} - -.wplace-theme-neon .wplace-section { - background: rgb(22 33 62 / 50%) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; -} - -.wplace-theme-neon .wplace-status-section { - background: rgb(22 33 62 / 50%) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; -} - -.wplace-theme-neon .wplace-section-title { - color: var(--wplace-text) !important; - text-shadow: 0 0 8px var(--wplace-text) !important; - text-transform: uppercase !important; - font-family: var(--wplace-font) !important; - font-size: 10px !important; -} - -.wplace-theme-neon .wplace-btn { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - text-shadow: 0 0 5px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - font-size: 9px !important; - text-transform: uppercase !important; -} - -/* Pixel blinking and neon glow animation for buttons */ -.wplace-theme-neon .wplace-btn:hover:not(:disabled) { - box-shadow: 0 0 15px var(--wplace-text), inset 0 0 15px rgb(0 255 65 / 10%) !important; - animation: pixel-blink 0.5s infinite, neon-glow 1s ease-in-out infinite alternate !important; -} - -.wplace-theme-neon .wplace-btn-start { - background: var(--wplace-secondary) !important; - border-color: var(--wplace-success) !important; - color: var(--wplace-success) !important; - text-shadow: 0 0 8px var(--wplace-success) !important; -} - -.wplace-theme-neon .wplace-btn-stop { - background: var(--wplace-secondary) !important; - border-color: var(--wplace-error) !important; - color: var(--wplace-error) !important; - text-shadow: 0 0 8px var(--wplace-error) !important; -} - -.wplace-theme-neon .wplace-btn-upload { - background: var(--wplace-secondary) !important; - border: 1px dashed var(--wplace-highlight) !important; - color: var(--wplace-highlight) !important; - text-shadow: 0 0 8px var(--wplace-highlight) !important; -} - -.wplace-theme-neon .wplace-btn-select { - background: var(--wplace-secondary) !important; - border-color: var(--wplace-highlight) !important; - color: var(--wplace-highlight) !important; - text-shadow: 0 0 8px var(--wplace-highlight) !important; -} - -.wplace-theme-neon .wplace-btn-file { - background: var(--wplace-secondary) !important; - border-color: var(--wplace-warning) !important; - color: var(--wplace-warning) !important; - text-shadow: 0 0 8px var(--wplace-warning) !important; -} - -.wplace-theme-neon .wplace-progress { - background: rgb(0 0 0 / 80%) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; -} - -.wplace-theme-neon .wplace-progress-bar { - background: linear-gradient(90deg, var(--wplace-success) 0%, var(--wplace-text) 100%) !important; - box-shadow: 0 0 10px var(--wplace-success) !important; -} - -.wplace-theme-neon .wplace-stat-value { - color: var(--wplace-text) !important; - text-shadow: 0 0 5px var(--wplace-text) !important; -} - -.wplace-theme-neon .status-success { - background: rgb(57 255 20 / 10%) !important; - border-color: var(--wplace-success) !important; - color: var(--wplace-success) !important; - box-shadow: 0 0 15px var(--wplace-success) !important; - text-shadow: 0 0 8px var(--wplace-success) !important; -} - -.wplace-theme-neon .status-error { - background: rgb(255 7 58 / 10%) !important; - border-color: var(--wplace-error) !important; - color: var(--wplace-error) !important; - box-shadow: 0 0 15px var(--wplace-error) !important; - text-shadow: 0 0 8px var(--wplace-error) !important; -} - -.wplace-theme-neon .status-default { - background: rgb(0 255 65 / 10%) !important; - border-color: var(--wplace-text) !important; - color: var(--wplace-text) !important; - text-shadow: 0 0 5px var(--wplace-text) !important; -} - -/* Settings dialog neon styling */ -.wplace-theme-neon #wplace-settings-container { - background: var(--wplace-primary) !important; - border: 2px solid var(--wplace-text) !important; - border-radius: 0 !important; - box-shadow: 0 0 30px var(--wplace-text), inset 0 0 30px rgb(0 255 65 / 10%) !important; - font-family: var(--wplace-font) !important; -} - -.wplace-theme-neon .wplace-settings-header { - background: var(--wplace-secondary) !important; - border-bottom: 1px solid var(--wplace-text) !important; -} - -.wplace-theme-neon .wplace-settings-header h3 { - color: var(--wplace-text) !important; - text-shadow: 0 0 10px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - font-size: 16px !important; - text-transform: uppercase !important; -} - -.wplace-theme-neon .wplace-settings-close-btn { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; -} - -.wplace-theme-neon .wplace-settings-close-btn:hover { - background: var(--wplace-error) !important; - box-shadow: 0 0 15px var(--wplace-error) !important; -} - -.wplace-theme-neon .wplace-settings-section-wrapper { - background: rgb(22 33 62 / 30%) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; -} - -.wplace-theme-neon .wplace-settings-select { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - font-size: 11px !important; -} - -.wplace-theme-neon .wplace-settings-section-label { - color: var(--wplace-text) !important; - text-shadow: 0 0 8px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - font-size: 12px !important; - text-transform: uppercase !important; -} - -.wplace-theme-neon .wplace-speed-value { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - text-shadow: 0 0 8px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important; -} - -.wplace-theme-neon .wplace-overlay-opacity-value { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - text-shadow: 0 0 5px var(--wplace-text) !important; -} - -/* Neon slider styling */ -.wplace-theme-neon .wplace-slider, -.wplace-theme-neon .wplace-speed-slider, -.wplace-theme-neon .wplace-overlay-opacity-slider { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important; -} - -.wplace-theme-neon .wplace-slider::-webkit-slider-thumb, -.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb, -.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb { - -webkit-appearance: none; - width: 16px; - height: 16px; - border-radius: 0 !important; - background: var(--wplace-slider-thumb-bg) !important; - border: 2px solid var(--wplace-text) !important; - cursor: pointer; - transition: all 0.2s ease; - box-shadow: 0 0 8px rgb(0 255 65 / 50%) !important; -} - -.wplace-theme-neon .wplace-slider::-webkit-slider-thumb:hover, -.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb:hover, -.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb:hover { - transform: scale(1.1); - box-shadow: 0 0 15px var(--wplace-text) !important; -} - -/* Scanline animation for neon theme */ -.wplace-theme-neon #wplace-image-bot-container::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, transparent, var(--wplace-text), transparent); - z-index: 1; - pointer-events: none; - animation: scanline 3s linear infinite; - opacity: 0.7; -} - - -.wplace-theme-neon #wplace-stats-container::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, transparent, var(--wplace-text), transparent); - z-index: 1; - pointer-events: none; - animation: scanline-stats 4s linear infinite; - opacity: 0.7; -} - - -/* Text glow animations */ -@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 scanline-stats { - 0% { - transform: translateY(-100%); - } - - 100% { - transform: translateY(300px); - } -} - -/* Dual control layout styling for neon theme */ -.wplace-theme-neon .wplace-dual-control-compact { - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; - margin: 6px 0; - flex-wrap: wrap; -} - -.wplace-theme-neon .wplace-slider-container-compact { - display: flex; - align-items: center; - gap: 6px; - flex: 1; - min-width: 160px; -} - -.wplace-theme-neon .wplace-speed-slider-container-compact { - display: flex; - align-items: center; - gap: 6px; - flex: 1; - min-width: 160px; -} - -.wplace-theme-neon .wplace-input-group-compact { - display: flex; - align-items: center; - gap: 3px; - flex-shrink: 0; -} - -.wplace-theme-neon .wplace-input-btn-compact { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - width: 18px; - height: 18px; - cursor: pointer; - font-weight: bold; - font-size: 10px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - text-shadow: 0 0 5px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; -} - -.wplace-theme-neon .wplace-input-btn-compact:hover { - box-shadow: 0 0 10px var(--wplace-text) !important; - animation: pixel-blink 0.5s infinite !important; -} - -.wplace-theme-neon .wplace-number-input-compact { - background: var(--wplace-secondary) !important; - border: 1px solid var(--wplace-text) !important; - border-radius: 0 !important; - color: var(--wplace-text) !important; - padding: 2px 4px; - font-size: 10px; - width: 40px; - text-align: center; - transition: all 0.2s ease; - height: 18px; - font-family: var(--wplace-font) !important; - text-shadow: 0 0 5px var(--wplace-text) !important; -} - -.wplace-theme-neon .wplace-number-input-compact:focus { - outline: none; - box-shadow: 0 0 10px var(--wplace-text) !important; - background: rgb(0 255 65 / 15%) !important; -} - -.wplace-theme-neon .wplace-input-label-compact { - color: var(--wplace-text) !important; - font-size: 9px; - margin-left: 3px; - white-space: nowrap; - text-shadow: 0 0 5px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - text-transform: uppercase; -} - -.wplace-theme-neon .wplace-batch-size-header { - margin-bottom: 6px; -} - -.wplace-theme-neon .wplace-batch-size-label { - color: var(--wplace-text) !important; - font-weight: 500; - font-size: 11px; - text-shadow: 0 0 8px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - text-transform: uppercase; -} - -/* Cooldown control styling for neon theme */ -.wplace-theme-neon .wplace-cooldown-control { - margin: 8px 0; -} - -.wplace-theme-neon .wplace-cooldown-control label { - color: var(--wplace-text) !important; - font-weight: 500; - font-size: 11px; - text-shadow: 0 0 8px var(--wplace-text) !important; - font-family: var(--wplace-font) !important; - text-transform: uppercase; - margin-bottom: 6px; - display: block; -} - -/* Resize dialog styling for neon theme */ -.wplace-theme-neon .resize-container { - background: #1a1a2e !important; - border: 3px solid #00ff41 !important; - border-radius: 0 !important; - box-shadow: 0 0 30px rgb(0 255 65 / 50%) !important; - font-family: 'Press Start 2P', monospace !important; -} - From 7b344e2d3900d84d14d769e55eb00aa81b8664cc Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:20:05 +0800 Subject: [PATCH 08/18] Delete lang directory --- lang/en.json | 154 ------------------------------------------- lang/fr.json | 144 ---------------------------------------- lang/id.json | 144 ---------------------------------------- lang/ja.json | 144 ---------------------------------------- lang/ko.json | 144 ---------------------------------------- lang/lang/es-MX.json | 144 ---------------------------------------- lang/pt.json | 143 ---------------------------------------- lang/ru.json | 143 ---------------------------------------- lang/tr.json | 144 ---------------------------------------- lang/uk.json | 144 ---------------------------------------- lang/vi.json | 144 ---------------------------------------- lang/zh-CN.json | 144 ---------------------------------------- lang/zh-TW.json | 144 ---------------------------------------- 13 files changed, 1880 deletions(-) delete mode 100644 lang/en.json delete mode 100644 lang/fr.json delete mode 100644 lang/id.json delete mode 100644 lang/ja.json delete mode 100644 lang/ko.json delete mode 100644 lang/lang/es-MX.json delete mode 100644 lang/pt.json delete mode 100644 lang/ru.json delete mode 100644 lang/tr.json delete mode 100644 lang/uk.json delete mode 100644 lang/vi.json delete mode 100644 lang/zh-CN.json delete mode 100644 lang/zh-TW.json diff --git a/lang/en.json b/lang/en.json deleted file mode 100644 index d2c493d..0000000 --- a/lang/en.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "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 deleted file mode 100644 index 8d0d2f5..0000000 --- a/lang/fr.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 9cad6e2..0000000 --- a/lang/id.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index d2fc973..0000000 --- a/lang/ja.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 570183b..0000000 --- a/lang/ko.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 8ca3e2b..0000000 --- a/lang/lang/es-MX.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index ebbc4f6..0000000 --- a/lang/pt.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "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 deleted file mode 100644 index 1bb8b16..0000000 --- a/lang/ru.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "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 deleted file mode 100644 index 3a12640..0000000 --- a/lang/tr.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 34171c5..0000000 --- a/lang/uk.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index ea79866..0000000 --- a/lang/vi.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 9058c37..0000000 --- a/lang/zh-CN.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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 deleted file mode 100644 index 384886e..0000000 --- a/lang/zh-TW.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "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": "繪製透明像素" -} From 16c0b31ef39a291266a45fb569a363ed221387e0 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 8 Sep 2025 03:24:17 +0800 Subject: [PATCH 09/18] Add files via upload --- Extension/Script-manager-fixed.js | 615 +++ Extension/Script-manager.js | 637 +++ Extension/auto-image-styles.css | 1922 +++++++ Extension/background.js | 462 ++ Extension/content-script.js | 301 + Extension/icons/icon128.png | Bin 0 -> 112618 bytes Extension/icons/icon16.png | Bin 0 -> 112618 bytes Extension/icons/icon32.png | Bin 0 -> 112618 bytes Extension/icons/icon48.png | Bin 0 -> 112618 bytes Extension/lang/de.json | 74 + Extension/lang/en.json | 154 + Extension/lang/fr.json | 144 + Extension/lang/id.json | 144 + Extension/lang/ja.json | 144 + Extension/lang/ko.json | 144 + Extension/lang/lang/es-MX.json | 144 + Extension/lang/pt.json | 143 + Extension/lang/ru.json | 143 + Extension/lang/tr.json | 144 + Extension/lang/uk.json | 144 + Extension/lang/vi.json | 144 + Extension/lang/zh-CN.json | 144 + Extension/lang/zh-TW.json | 144 + Extension/manifest.json | 55 + Extension/popup/popup.css | 387 ++ Extension/popup/popup.html | 48 + Extension/popup/popup.js | 184 + Extension/scripts/Auto-Farm.js | 650 +++ Extension/scripts/Auto-Image.js | 8220 ++++++++++++++++++++++++++++ Extension/scripts/Auto-Repair.js | 2787 ++++++++++ Extension/themes/acrylic.css | 603 ++ Extension/themes/classic-light.css | 598 ++ Extension/themes/classic.css | 725 +++ Extension/themes/neon.css | 519 ++ 34 files changed, 20667 insertions(+) create mode 100644 Extension/Script-manager-fixed.js create mode 100644 Extension/Script-manager.js create mode 100644 Extension/auto-image-styles.css create mode 100644 Extension/background.js create mode 100644 Extension/content-script.js create mode 100644 Extension/icons/icon128.png create mode 100644 Extension/icons/icon16.png create mode 100644 Extension/icons/icon32.png create mode 100644 Extension/icons/icon48.png create mode 100644 Extension/lang/de.json create mode 100644 Extension/lang/en.json create mode 100644 Extension/lang/fr.json create mode 100644 Extension/lang/id.json create mode 100644 Extension/lang/ja.json create mode 100644 Extension/lang/ko.json create mode 100644 Extension/lang/lang/es-MX.json create mode 100644 Extension/lang/pt.json create mode 100644 Extension/lang/ru.json create mode 100644 Extension/lang/tr.json create mode 100644 Extension/lang/uk.json create mode 100644 Extension/lang/vi.json create mode 100644 Extension/lang/zh-CN.json create mode 100644 Extension/lang/zh-TW.json create mode 100644 Extension/manifest.json create mode 100644 Extension/popup/popup.css create mode 100644 Extension/popup/popup.html create mode 100644 Extension/popup/popup.js create mode 100644 Extension/scripts/Auto-Farm.js create mode 100644 Extension/scripts/Auto-Image.js create mode 100644 Extension/scripts/Auto-Repair.js create mode 100644 Extension/themes/acrylic.css create mode 100644 Extension/themes/classic-light.css create mode 100644 Extension/themes/classic.css create mode 100644 Extension/themes/neon.css diff --git a/Extension/Script-manager-fixed.js b/Extension/Script-manager-fixed.js new file mode 100644 index 0000000..4e752d2 --- /dev/null +++ b/Extension/Script-manager-fixed.js @@ -0,0 +1,615 @@ +// WPlace AutoBOT Script Manager - Neon Cyberpunk Edition +// Fixed version with proper content script communication + +(function() { + 'use strict'; + + console.log('%c🎮 WPlace AutoBOT Script Manager v2.0 - FIXED', 'color: #00ff41; font-weight: bold; font-size: 16px;'); + console.log('%c⚡ Neon Cyberpunk Theme with Content Script Bridge', 'color: #00ff41; font-weight: bold;'); + + // Available scripts configuration + const AVAILABLE_SCRIPTS = [ + { + name: 'Auto-Image.js', + title: 'Auto Image Placer', + description: 'Automatically place images on the canvas with smart positioning', + category: 'Automation', + primary: true + }, + { + name: 'Auto-Farm.js', + title: 'Auto Farm Bot', + description: 'Automated farming and resource collection', + category: 'Automation' + }, + { + name: 'Auto-Repair.js', + title: 'Auto Repair Tool', + description: 'Automatically repair damaged pixels and structures', + category: 'Utility' + } + ]; + + // Neon cyberpunk CSS styles + const NEON_STYLES = ` + @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); + + @keyframes neon-glow { + 0%, 100% { box-shadow: 0 0 5px #00ff41, 0 0 10px #00ff41, 0 0 15px #00ff41; } + 50% { box-shadow: 0 0 10px #00ff41, 0 0 20px #00ff41, 0 0 30px #00ff41; } + } + + @keyframes pixel-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.7; } + } + + @keyframes scanline { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100vw); } + } + + @keyframes neon-fade-in { + from { opacity: 0; transform: scale(0.8) translateY(20px); } + to { opacity: 1; transform: scale(1) translateY(0); } + } + + @keyframes neon-fade-out { + from { opacity: 1; transform: scale(1) translateY(0); } + to { opacity: 0; transform: scale(0.8) translateY(-20px); } + } + + @keyframes backdrop-fade-in { + from { opacity: 0; } + to { opacity: 1; } + } + + @keyframes backdrop-fade-out { + from { opacity: 1; } + to { opacity: 0; } + } + + .script-manager-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(26, 26, 46, 0.8); + backdrop-filter: blur(5px); + z-index: 999998; + animation: backdrop-fade-in 0.3s ease-out forwards; + } + + .script-manager-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 90%; + max-width: 600px; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); + border: 2px solid #00ff41; + border-radius: 0; + z-index: 999999; + font-family: 'Press Start 2P', monospace; + color: #00ff41; + box-shadow: 0 0 30px rgba(0, 255, 65, 0.5), inset 0 0 30px rgba(0, 255, 65, 0.1); + animation: neon-fade-in 0.3s ease-out forwards; + outline: none; + } + + .script-manager-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, #00ff41, transparent); + z-index: 1; + pointer-events: none; + animation: scanline 3s linear infinite; + opacity: 0.7; + } + + .script-manager-header { + background: #16213e; + border-bottom: 2px solid #00ff41; + padding: 15px 20px; + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + } + + .header-content { + display: flex; + align-items: center; + gap: 15px; + } + + .header-icon { + width: 32px; + height: 32px; + border-radius: 6px; + box-shadow: 0 0 15px rgba(0, 255, 65, 0.4); + transition: all 0.3s ease; + animation: pixel-blink 3s infinite; + } + + .header-icon:hover { + transform: scale(1.1); + box-shadow: 0 0 25px rgba(0, 255, 65, 0.6); + } + + .script-manager-title { + color: #00ff41; + font-size: 14px; + text-shadow: 0 0 15px #00ff41; + margin: 0; + text-transform: uppercase; + letter-spacing: 2px; + } + + .script-manager-close { + background: transparent; + border: 2px solid #ff073a; + color: #ff073a; + width: 40px; + height: 40px; + border-radius: 0; + font-family: 'Press Start 2P', monospace; + font-size: 16px; + cursor: pointer; + transition: all 0.3s ease; + text-shadow: 0 0 10px #ff073a; + } + + .script-manager-close:hover { + background: #ff073a; + color: #000; + box-shadow: 0 0 20px rgba(255, 7, 58, 0.6); + transform: scale(1.1); + } + + .script-manager-content { + padding: 20px; + min-height: 200px; + max-height: 400px; + overflow-y: auto; + } + + .script-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 15px; + } + + .script-card { + background: linear-gradient(135deg, #1a1a2e, #16213e); + border: 1px solid #00ff41; + padding: 15px; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + } + + .script-card::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(0, 255, 65, 0.1), transparent); + transition: left 0.5s ease; + } + + .script-card:hover::before { + left: 100%; + } + + .script-card:hover { + border-color: #39ff14; + box-shadow: 0 0 20px rgba(0, 255, 65, 0.4); + transform: translateY(-2px); + } + + .script-card.primary { + border-color: #39ff14; + box-shadow: 0 0 15px rgba(57, 255, 20, 0.3); + } + + .script-card.primary::after { + content: '⭐ RECOMMENDED'; + position: absolute; + top: 5px; + right: 5px; + background: #39ff14; + color: #000; + padding: 2px 5px; + font-size: 6px; + text-shadow: none; + } + + .script-name { + color: #00ff41; + font-size: 10px; + font-weight: bold; + text-shadow: 0 0 10px #00ff41; + margin-bottom: 8px; + text-transform: uppercase; + } + + .script-description { + color: #00ff41dd; + font-size: 7px; + line-height: 1.4; + margin-bottom: 10px; + text-shadow: 0 0 5px #00ff41; + } + + .script-category { + background: rgba(255, 107, 53, 0.2); + border: 1px solid #ff6b35; + color: #ff6b35; + padding: 3px 8px; + font-size: 7px; + text-transform: uppercase; + letter-spacing: 1px; + text-shadow: 0 0 5px #ff6b35; + display: inline-block; + } + + .script-manager-footer { + background: #16213e; + border-top: 2px solid #00ff41; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .status-text { + color: #00ff41dd; + font-size: 8px; + text-shadow: 0 0 5px #00ff41; + } + + .action-buttons { + display: flex; + gap: 10px; + } + + .neon-btn { + background: #16213e; + border: 2px solid #00ff41; + border-radius: 0; + color: #00ff41; + padding: 8px 15px; + font-family: 'Press Start 2P', monospace; + font-size: 8px; + text-transform: uppercase; + cursor: pointer; + transition: all 0.3s ease; + text-shadow: 0 0 8px #00ff41; + letter-spacing: 1px; + } + + .neon-btn:hover { + background: #00ff41; + color: #000; + box-shadow: 0 0 20px rgba(0, 255, 65, 0.6); + transform: scale(1.05); + } + + .neon-btn.secondary { + border-color: #ff6b35; + color: #ff6b35; + text-shadow: 0 0 8px #ff6b35; + } + + .neon-btn.secondary:hover { + background: #ff6b35; + color: #000; + box-shadow: 0 0 20px rgba(255, 107, 53, 0.6); + } + + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 200px; + gap: 20px; + } + + .loading-spinner { + width: 40px; + height: 40px; + border: 2px solid #16213e; + border-top: 2px solid #00ff41; + border-radius: 50%; + animation: spin 1s linear infinite; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .loading-text { + color: #00ff41; + font-size: 10px; + text-align: center; + text-shadow: 0 0 10px #00ff41; + animation: pixel-blink 1.5s infinite; + } + `; + + // ESC key handler for closing + function handleEscKey(event) { + if (event.key === 'Escape') { + closeScriptManager(); + } + } + + // Execute script function with content script communication + async function executeScript(scriptName) { + console.group(`%c🚀 Executing ${scriptName}`, 'color: #00ff41; font-weight: bold;'); + + try { + showLoading(`Launching ${scriptName}...`); + + console.log('%c🔄 Script Manager → Content Script → Background Script → Execute', 'color: #ff6b35; font-weight: bold;'); + console.log('Script Manager runs in page context, using postMessage bridge to extension'); + + // Create response promise + const responsePromise = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error('Timeout: No response from extension after 10 seconds')); + }, 10000); + + const handleResponse = (event) => { + if (event.source !== window || !event.data) return; + + if (event.data.type === 'AUTOBOT_SCRIPT_RESPONSE') { + clearTimeout(timeoutId); + window.removeEventListener('message', handleResponse); + + if (event.data.success) { + resolve(event.data); + } else { + reject(new Error(event.data.error || 'Script execution failed')); + } + } + }; + + window.addEventListener('message', handleResponse); + }); + + // Send message to content script + console.log(`%c📤 Sending postMessage to content script`, 'color: #06b6d4;'); + console.log(` Script: ${scriptName}`); + + window.postMessage({ + type: 'AUTOBOT_EXECUTE_SCRIPT', + scriptName: scriptName, + source: 'script-manager', + timestamp: Date.now() + }, '*'); + + // Wait for response + const response = await responsePromise; + + console.log(`%c✅ ${scriptName} executed successfully!`, 'color: #39ff14; font-weight: bold;'); + showSuccess(`${scriptName} launched successfully!`); + + setTimeout(() => { + closeScriptManager(); + }, 2000); + + } catch (error) { + console.error(`%c❌ Failed to execute ${scriptName}:`, 'color: #ff073a; font-weight: bold;', error); + showError(`Failed to launch ${scriptName}: ${error.message}`); + } finally { + console.groupEnd(); + } + } + + // UI Management functions + function showLoading(message) { + const container = document.getElementById('script-manager-content'); + if (!container) return; + + container.innerHTML = ` +
+
+
${message}
+
+ `; + } + + function showSuccess(message) { + const statusText = document.querySelector('.status-text'); + if (statusText) { + statusText.textContent = `✅ ${message}`; + statusText.style.color = '#39ff14'; + statusText.style.textShadow = '0 0 10px #39ff14'; + } + } + + function showError(message) { + const statusText = document.querySelector('.status-text'); + if (statusText) { + statusText.textContent = `❌ ${message}`; + statusText.style.color = '#ff073a'; + statusText.style.textShadow = '0 0 10px #ff073a'; + } + + // Reset the content to show scripts again + setTimeout(() => { + renderScripts(); + }, 3000); + } + + // Render scripts grid + function renderScripts() { + const container = document.getElementById('script-manager-content'); + if (!container) return; + + const scriptGrid = AVAILABLE_SCRIPTS.map(script => ` +
+
${script.title}
+

${script.description}

+ ${script.category} +
+ `).join(''); + + container.innerHTML = ` +
+ ${scriptGrid} +
+ `; + } + + // Close script manager + function closeScriptManager() { + const container = document.getElementById('script-manager-container'); + const backdrop = document.getElementById('script-manager-backdrop'); + + if (container) { + container.style.animation = 'neon-fade-out 0.3s ease-in forwards'; + setTimeout(() => { + container.remove(); + }, 300); + } + + if (backdrop) { + backdrop.style.animation = 'backdrop-fade-out 0.3s ease-in forwards'; + setTimeout(() => { + backdrop.remove(); + }, 300); + } + + // Remove ESC key listener + document.removeEventListener('keydown', handleEscKey); + + console.log('%c👋 Script Manager closed', 'color: #ff6b35;'); + } + + // Show script manager interface + function showScriptManager() { + // Remove any existing manager + const existing = document.getElementById('script-manager-container'); + if (existing) existing.remove(); + + const existingBackdrop = document.getElementById('script-manager-backdrop'); + if (existingBackdrop) existingBackdrop.remove(); + + console.log('%c🎮 Opening Script Manager with Neon Theme', 'color: #00ff41; font-weight: bold;'); + + // Get icon URL for display + let iconUrl = ''; + try { + if (chrome && chrome.runtime && chrome.runtime.getURL) { + iconUrl = chrome.runtime.getURL('icons/icon32.png'); + console.log('📷 Icon URL:', iconUrl); + } + } catch (e) { + console.log('Extension context not available for icon'); + } + + // Inject styles + if (!document.getElementById('script-manager-styles')) { + const styleElement = document.createElement('style'); + styleElement.id = 'script-manager-styles'; + styleElement.textContent = NEON_STYLES; + document.head.appendChild(styleElement); + } + + // Create backdrop + const backdrop = document.createElement('div'); + backdrop.id = 'script-manager-backdrop'; + backdrop.className = 'script-manager-backdrop'; + backdrop.addEventListener('click', closeScriptManager); + + // Create container + const container = document.createElement('div'); + container.id = 'script-manager-container'; + container.className = 'script-manager-container'; + + container.innerHTML = ` +
+
+ ${iconUrl ? `AutoBOT` : ''} +

⚡ WPlace AutoBOT Script Manager ⚡

+
+ +
+
+ +
+ + `; + + // Add to page + document.body.appendChild(backdrop); + document.body.appendChild(container); + + // Render scripts + renderScripts(); + + // Add ESC key listener + document.addEventListener('keydown', handleEscKey); + + // Focus container for accessibility + container.focus(); + + console.log('%c✅ Script Manager opened successfully', 'color: #39ff14; font-weight: bold;'); + } + + // Make functions globally available + window.executeScript = executeScript; + window.closeScriptManager = closeScriptManager; + window.showScriptManager = showScriptManager; + + // Test content script communication + console.group('%c🔍 Content Script Communication Test', 'color: #ff6b35; font-weight: bold;'); + console.log('Testing if content script can receive postMessage...'); + + // Listen for test response + const testTimeout = setTimeout(() => { + console.warn('❌ No response from content script - communication may not work'); + console.groupEnd(); + }, 2000); + + window.addEventListener('message', function testHandler(event) { + if (event.data && event.data.type === 'AUTOBOT_CONTENT_SCRIPT_READY') { + clearTimeout(testTimeout); + window.removeEventListener('message', testHandler); + console.log('✅ Content script communication confirmed!'); + console.groupEnd(); + } + }); + + // Send test message + window.postMessage({ + type: 'AUTOBOT_TEST_CONNECTION', + source: 'script-manager' + }, '*'); + + // Auto-start the script manager + console.log('%c🎯 Auto-launching Script Manager...', 'color: #00ff41; font-weight: bold;'); + showScriptManager(); + + console.log('%c🚀 WPlace AutoBOT Script Manager v2.0 Ready!', 'color: #39ff14; font-weight: bold; font-size: 16px;'); +})(); diff --git a/Extension/Script-manager.js b/Extension/Script-manager.js new file mode 100644 index 0000000..98af2dc --- /dev/null +++ b/Extension/Script-manager.js @@ -0,0 +1,637 @@ +// ==UserScript== +// @name WPlace AutoBOT Script Manager +// @namespace http://tampermonkey.net/ +// @version 2025-09-08.1 +// @description Script manager and launcher for WPlace AutoBOT +// @author TH3C0D3R +// @match https://wplace.live/* +// @grant none +// @icon +// ==/UserScript== + +; (async () => { + console.log('%c🚀 WPlace AutoBOT Script Manager Loading...', 'color: #00ff41; font-weight: bold; font-size: 16px;'); + + // Available scripts configuration + const AVAILABLE_SCRIPTS = [ + { + name: 'Auto-Farm.js', + displayName: '🌾 Auto Farm', + description: 'Automated farming and pixel painting', + icon: '🌾', + category: 'automation' + }, + { + name: 'Auto-Image.js', + displayName: '🖼️ Auto Image', + description: 'Automated image processing and placement', + icon: '🖼️', + category: 'automation' + }, + { + name: 'Auto-Repair.js', + displayName: '🔧 Auto Repair', + description: 'Automated repair and maintenance tasks', + icon: '🔧', + category: 'utility' + } + ]; + + // Neon theme styling + const NEON_STYLES = ` + @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); + + .script-manager-container { + position: fixed !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + background: #1a1a2e; + border: 3px solid #00ff41; + border-radius: 0; + box-shadow: + 0 0 30px rgb(0 255 65 / 50%), + inset 0 0 30px rgb(0 255 65 / 10%), + 0 0 0 1px #00ff41; + font-family: 'Press Start 2P', monospace, 'Courier New'; + z-index: 10001 !important; + min-width: 600px; + max-width: 800px; + max-height: 80vh; + overflow: hidden; + color: #00ff41; + animation: neon-pulse 2s ease-in-out infinite alternate; + } + + .script-manager-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, #00ff41, transparent); + z-index: 1; + pointer-events: none; + animation: scanline 3s linear infinite; + opacity: 0.7; + } + + .script-manager-header { + background: #16213e; + border-bottom: 2px solid #00ff41; + padding: 15px 20px; + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + } + + .header-content { + display: flex; + align-items: center; + gap: 15px; + } + + .header-icon { + width: 32px; + height: 32px; + border-radius: 6px; + box-shadow: 0 0 15px rgba(0, 255, 65, 0.4); + transition: all 0.3s ease; + animation: pixel-blink 3s infinite; + } + + .header-icon:hover { + transform: scale(1.1); + box-shadow: 0 0 25px rgba(0, 255, 65, 0.6); + } + + .script-manager-title { + color: #00ff41; + font-size: 14px; + text-shadow: 0 0 15px #00ff41; + margin: 0; + text-transform: uppercase; + letter-spacing: 2px; + animation: text-glow 2s ease-in-out infinite alternate; + } + + .script-manager-close { + background: #16213e; + border: 2px solid #ff073a; + border-radius: 0; + color: #ff073a; + width: 30px; + height: 30px; + cursor: pointer; + font-family: 'Press Start 2P', monospace; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + text-shadow: 0 0 10px #ff073a; + } + + .script-manager-close:hover { + background: #ff073a; + color: #1a1a2e; + box-shadow: 0 0 20px #ff073a; + animation: pixel-blink 0.5s infinite; + } + + .script-manager-content { + padding: 20px; + max-height: 60vh; + overflow-y: auto; + background: linear-gradient(45deg, rgba(0,255,65,0.03) 0%, rgba(22,33,62,0.05) 100%); + } + + .script-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 15px; + margin-bottom: 20px; + } + + .script-card { + background: #16213e; + border: 2px solid #00ff41; + border-radius: 0; + padding: 15px; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + } + + .script-card::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, transparent, rgba(0,255,65,0.1), transparent); + transform: rotate(45deg); + transition: all 0.5s ease; + opacity: 0; + } + + .script-card:hover::before { + animation: neon-sweep 1s ease-in-out; + } + + .script-card:hover { + background: rgba(0,255,65,0.1); + box-shadow: + 0 0 25px rgb(0 255 65 / 60%), + inset 0 0 25px rgb(0 255 65 / 15%); + transform: translateY(-3px); + animation: card-glow 0.5s ease-in-out infinite alternate; + } + + .script-card-header { + display: flex; + align-items: center; + margin-bottom: 10px; + } + + .script-icon { + font-size: 24px; + margin-right: 10px; + filter: drop-shadow(0 0 10px #00ff41); + } + + .script-title { + color: #00ff41; + font-size: 11px; + text-shadow: 0 0 10px #00ff41; + text-transform: uppercase; + letter-spacing: 1px; + margin: 0; + } + + .script-description { + color: #00ff41dd; + font-size: 8px; + line-height: 1.4; + text-shadow: 0 0 5px #00ff41; + margin-bottom: 15px; + } + + .script-category { + background: rgba(255, 107, 53, 0.2); + border: 1px solid #ff6b35; + color: #ff6b35; + padding: 3px 8px; + font-size: 7px; + text-transform: uppercase; + letter-spacing: 1px; + text-shadow: 0 0 5px #ff6b35; + display: inline-block; + } + + .script-manager-footer { + background: #16213e; + border-top: 2px solid #00ff41; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .status-text { + color: #00ff41dd; + font-size: 8px; + text-shadow: 0 0 5px #00ff41; + } + + .action-buttons { + display: flex; + gap: 10px; + } + + .neon-btn { + background: #16213e; + border: 2px solid #00ff41; + border-radius: 0; + color: #00ff41; + padding: 8px 15px; + font-family: 'Press Start 2P', monospace; + font-size: 8px; + text-transform: uppercase; + cursor: pointer; + transition: all 0.3s ease; + text-shadow: 0 0 8px #00ff41; + letter-spacing: 1px; + } + + .neon-btn:hover { + background: rgba(0,255,65,0.1); + box-shadow: 0 0 20px rgb(0 255 65 / 60%); + animation: pixel-blink 0.5s infinite; + } + + .neon-btn.secondary { + border-color: #ff6b35; + color: #ff6b35; + text-shadow: 0 0 8px #ff6b35; + } + + .neon-btn.secondary:hover { + background: rgba(255, 107, 53, 0.1); + box-shadow: 0 0 20px rgb(255 107 53 / 60%); + } + + .script-manager-backdrop { + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + background: rgba(0, 0, 0, 0.8); + z-index: 10000 !important; + backdrop-filter: blur(5px); + animation: backdrop-fade-in 0.3s ease-out; + } + + /* Loading animation */ + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + } + + .loading-spinner { + width: 40px; + height: 40px; + border: 3px solid #16213e; + border-top: 3px solid #00ff41; + border-radius: 0; + animation: neon-spin 1s linear infinite; + margin-bottom: 15px; + box-shadow: 0 0 20px rgb(0 255 65 / 50%); + } + + .loading-text { + color: #00ff41; + font-size: 8px; + text-shadow: 0 0 10px #00ff41; + text-transform: uppercase; + letter-spacing: 2px; + animation: text-pulse 1.5s ease-in-out infinite; + } + + /* Custom scrollbar */ + .script-manager-content::-webkit-scrollbar { + width: 12px; + } + + .script-manager-content::-webkit-scrollbar-track { + background: #16213e; + border: 1px solid #00ff41; + } + + .script-manager-content::-webkit-scrollbar-thumb { + background: #00ff41; + border-radius: 0; + box-shadow: 0 0 10px #00ff41; + } + + .script-manager-content::-webkit-scrollbar-thumb:hover { + background: #39ff14; + box-shadow: 0 0 15px #39ff14; + } + + /* Animations */ + @keyframes neon-pulse { + 0% { box-shadow: 0 0 30px rgb(0 255 65 / 50%), inset 0 0 30px rgb(0 255 65 / 10%), 0 0 0 1px #00ff41; } + 100% { box-shadow: 0 0 40px rgb(0 255 65 / 70%), inset 0 0 40px rgb(0 255 65 / 15%), 0 0 0 1px #00ff41; } + } + + @keyframes text-glow { + 0% { text-shadow: 0 0 15px #00ff41; } + 100% { text-shadow: 0 0 25px #00ff41, 0 0 35px #00ff41; } + } + + @keyframes pixel-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.7; } + } + + @keyframes scanline { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(400px); } + } + + @keyframes neon-sweep { + 0% { opacity: 0; transform: translateX(-100%) translateY(-100%) rotate(45deg); } + 50% { opacity: 1; } + 100% { opacity: 0; transform: translateX(100%) translateY(100%) rotate(45deg); } + } + + @keyframes card-glow { + 0% { box-shadow: 0 0 25px rgb(0 255 65 / 60%), inset 0 0 25px rgb(0 255 65 / 15%); } + 100% { box-shadow: 0 0 35px rgb(0 255 65 / 80%), inset 0 0 35px rgb(0 255 65 / 20%); } + } + + @keyframes neon-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + @keyframes text-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } + } + + @keyframes backdrop-fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } + } + + /* Responsive design */ + @media (max-width: 768px) { + .script-manager-container { + min-width: 90vw; + max-width: 95vw; + } + + .script-grid { + grid-template-columns: 1fr; + } + + .script-manager-title { + font-size: 10px; + } + } + `; + + // Execute script function - Fixed to work like extension popup + async function executeScript(scriptName) { + console.group(`%c🚀 Executing ${scriptName}`, 'color: #00ff41; font-weight: bold;'); + + try { + // Show loading in the UI + showLoading(`Launching ${scriptName}...`); + + // The Script Manager runs in MAIN world context and doesn't have direct Chrome API access + // Instead, we need to communicate back to the content script which can use Chrome APIs + console.log('%c🔄 Script Manager context - delegating to content script', 'color: #ff6b35;'); + + // Create a custom event to communicate with the content script + const executeEvent = new CustomEvent('autobot-execute-script', { + detail: { scriptName: scriptName } + }); + + // Dispatch the event to the content script + window.dispatchEvent(executeEvent); + + // Show success immediately since we're delegating + console.log(`%c✅ ${scriptName} execution delegated to content script`, 'color: #39ff14; font-weight: bold;'); + showSuccess(`${scriptName} execution started!`); + + // Auto-close after success + setTimeout(() => { + closeScriptManager(); + }, 1500); + + } catch (error) { + console.error(`%c❌ Failed to execute ${scriptName}:`, 'color: #ff073a; font-weight: bold;', error); + showError(`Failed to launch ${scriptName}: ${error.message}`); + } finally { + console.groupEnd(); + } + } + + // UI Management functions + function showLoading(message) { + const container = document.getElementById('script-manager-content'); + if (!container) return; + + container.innerHTML = ` +
+
+
${message}
+
+ `; + } + + function showSuccess(message) { + const statusText = document.querySelector('.status-text'); + if (statusText) { + statusText.textContent = `✅ ${message}`; + statusText.style.color = '#39ff14'; + statusText.style.textShadow = '0 0 10px #39ff14'; + } + } + + function showError(message) { + const statusText = document.querySelector('.status-text'); + if (statusText) { + statusText.textContent = `❌ ${message}`; + statusText.style.color = '#ff073a'; + statusText.style.textShadow = '0 0 10px #ff073a'; + } + + // Reset the content to show scripts again + setTimeout(() => { + renderScripts(); + }, 3000); + } + + function renderScripts() { + const container = document.getElementById('script-manager-content'); + if (!container) return; + + const scriptGrid = AVAILABLE_SCRIPTS.map(script => ` +
+
+
${script.icon}
+

${script.displayName}

+
+

${script.description}

+ ${script.category} +
+ `).join(''); + + container.innerHTML = ` +
+ ${scriptGrid} +
+ `; + } + + // Close script manager + function closeScriptManager() { + const container = document.getElementById('script-manager-container'); + const backdrop = document.getElementById('script-manager-backdrop'); + + if (container) { + container.style.animation = 'neon-fade-out 0.3s ease-in forwards'; + setTimeout(() => { + container.remove(); + }, 300); + } + + if (backdrop) { + backdrop.style.animation = 'backdrop-fade-out 0.3s ease-in forwards'; + setTimeout(() => { + backdrop.remove(); + }, 300); + } + + // Remove ESC key listener + document.removeEventListener('keydown', handleEscKey); + + console.log('%c👋 Script Manager closed', 'color: #ff6b35;'); + } + + // ESC key handler + function handleEscKey(event) { + if (event.key === 'Escape') { + closeScriptManager(); + } + } + + // Main function to show script manager + function showScriptManager() { + // Remove any existing manager + const existing = document.getElementById('script-manager-container'); + if (existing) existing.remove(); + + const existingBackdrop = document.getElementById('script-manager-backdrop'); + if (existingBackdrop) existingBackdrop.remove(); + + console.log('%c🎮 Opening Script Manager with Neon Theme', 'color: #00ff41; font-weight: bold;'); + + // Get icon URL for display + let iconUrl = ''; + try { + if (chrome && chrome.runtime && chrome.runtime.getURL) { + iconUrl = chrome.runtime.getURL('icons/icon32.png'); + console.log('📷 Icon URL:', iconUrl); + } + } catch (e) { + console.log('Extension context not available for icon'); + } + + // Inject styles + if (!document.getElementById('script-manager-styles')) { + const styleElement = document.createElement('style'); + styleElement.id = 'script-manager-styles'; + styleElement.textContent = NEON_STYLES; + document.head.appendChild(styleElement); + } + + // Create backdrop + const backdrop = document.createElement('div'); + backdrop.id = 'script-manager-backdrop'; + backdrop.className = 'script-manager-backdrop'; + backdrop.addEventListener('click', closeScriptManager); + + // Create container + const container = document.createElement('div'); + container.id = 'script-manager-container'; + container.className = 'script-manager-container'; + + container.innerHTML = ` +
+
+ ${iconUrl ? `AutoBOT` : ''} +

⚡ WPlace AutoBOT Script Manager ⚡

+
+ +
+
+ +
+ + `; + + // Add to page + document.body.appendChild(backdrop); + document.body.appendChild(container); + + // Debug: Check positioning + console.log('%c🔍 Script Manager Positioning Debug:', 'color: #ff6b35; font-weight: bold;'); + console.log(` - Container position: ${getComputedStyle(container).position}`); + console.log(` - Container top: ${getComputedStyle(container).top}`); + console.log(` - Container left: ${getComputedStyle(container).left}`); + console.log(` - Container transform: ${getComputedStyle(container).transform}`); + console.log(` - Container z-index: ${getComputedStyle(container).zIndex}`); + console.log(` - Backdrop z-index: ${getComputedStyle(backdrop).zIndex}`); + + // Render scripts + renderScripts(); + + // Add ESC key listener + document.addEventListener('keydown', handleEscKey); + + // Focus container for accessibility + container.focus(); + + console.log('%c✅ Script Manager opened successfully', 'color: #39ff14; font-weight: bold;'); + } + + // Make functions globally available + window.executeScript = executeScript; + window.closeScriptManager = closeScriptManager; + window.showScriptManager = showScriptManager; + + // Auto-start the script manager + console.log('%c🎯 Auto-launching Script Manager...', 'color: #00ff41; font-weight: bold;'); + showScriptManager(); + + console.log('%c🚀 WPlace AutoBOT Script Manager Ready!', 'color: #39ff14; font-weight: bold; font-size: 16px;'); +})(); diff --git a/Extension/auto-image-styles.css b/Extension/auto-image-styles.css new file mode 100644 index 0000000..3e45a67 --- /dev/null +++ b/Extension/auto-image-styles.css @@ -0,0 +1,1922 @@ +/* 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) */ +/* ========================= */ + +/* NOTE: Themes are now loaded dynamically from extension local resources + instead of using @import from CDN. The extension will inject theme CSS + directly when needed using window.applyTheme() function. + + Previous CDN imports (now removed): + - https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/classic.css + - https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/classic-light.css + - https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/acrylic.css + - https://wplace-autobot.github.io/WPlace-AutoBOT/main/themes/neon.css + + These are now loaded from local extension files for better performance + and offline functionality. */ + +/* 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/Extension/background.js b/Extension/background.js new file mode 100644 index 0000000..2c904b1 --- /dev/null +++ b/Extension/background.js @@ -0,0 +1,462 @@ +// Background service worker for WPlace AutoBOT Extension + +// Test resource loading on extension start +chrome.runtime.onStartup.addListener(async () => { + console.log('🚀 WPlace AutoBOT Extension started'); + const resources = await loadExtensionResources(); + console.log('📦 Initial resource test:', resources); +}); + +chrome.runtime.onInstalled.addListener(async () => { + console.log('🔧 WPlace AutoBOT Extension installed'); + const resources = await loadExtensionResources(); + console.log('📦 Installation resource test:', resources); +}); + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === 'executeScript') { + // Get tabId from sender or request + const tabId = request.tabId || sender.tab?.id; + + if (!tabId) { + sendResponse({ success: false, error: 'Could not determine target tab' }); + return; + } + + // Use IIFE for async handling + (async () => { + try { + await executeLocalScript(request.scriptName, tabId); + sendResponse({ success: true }); + } catch (error) { + sendResponse({ success: false, error: error.message }); + } + })(); + + return true; // Important: indicates async response + } +}); + +async function executeLocalScript(scriptName, tabId) { + try { + console.log(`Loading script: ${scriptName}`); + + // Determine script path - Script-manager files are in root, others in scripts/ + let scriptUrl; + if (scriptName === 'Script-manager.js' || scriptName === 'Script-manager-fixed.js') { + scriptUrl = chrome.runtime.getURL(scriptName); + } else { + scriptUrl = chrome.runtime.getURL(`scripts/${scriptName}`); + } + + const response = await fetch(scriptUrl); + + if (!response.ok) { + throw new Error(`Failed to load script: ${response.status} ${response.statusText}`); + } + + const scriptCode = await response.text(); + console.log(`Script loaded: ${scriptCode.length} characters`); + + // Load theme and language resources + const resources = await loadExtensionResources(); + + // Execute in MAIN world context (bypasses CSP) + await chrome.scripting.executeScript({ + target: { tabId: tabId }, + world: "MAIN", // Key: executes in page context, not extension context + func: (code, name, themeCSS, languages) => { + console.log(`%c🚀 Executing ${name}...`, 'color: #4ade80; font-weight: bold; font-size: 14px;'); + + // Create detailed resource report + console.group(`%c📊 WPlace AutoBOT Resource Report for ${name}`, 'color: #3b82f6; font-weight: bold; font-size: 16px;'); + + // Debug: Log what resources we received + console.log(`%c📦 Raw Resources Received:`, 'color: #8b5cf6; font-weight: bold;'); + console.log(` - Themes object:`, themeCSS); + console.log(` - Languages object:`, languages); + console.log(` - Theme count: ${Object.keys(themeCSS || {}).length}`); + console.log(` - Language count: ${Object.keys(languages || {}).length}`); + + // Inject CSS themes if available + if (themeCSS && Object.keys(themeCSS).length > 0) { + console.group(`%c🎨 Theme Processing`, 'color: #8b5cf6; font-weight: bold;'); + console.log(`%c📁 Loading ${Object.keys(themeCSS).length} theme files from extension local storage...`, 'color: #8b5cf6;'); + + // Create a global themes object + window.AUTOBOT_THEMES = themeCSS; + + // Log detailed theme information + Object.entries(themeCSS).forEach(([filename, content]) => { + console.log(`%c📄 Theme File: ${filename}`, 'color: #8b5cf6; font-weight: bold;'); + console.log(` 📏 Size: ${content.length.toLocaleString()} characters`); + console.log(` 📍 Source: Extension local file (chrome-extension://)`); + console.log(` 🔗 Full path: themes/${filename}`); + + // Show first few lines as preview + const preview = content.substring(0, 200).split('\\n').slice(0, 3).join('\\n'); + console.log(` 👀 Preview: ${preview}${content.length > 200 ? '...' : ''}`); + console.log(` ✅ Status: Loaded successfully`); + }); + + // Inject auto-image-styles.css if available + if (themeCSS['auto-image-styles.css']) { + const autoImageContent = themeCSS['auto-image-styles.css']; + const styleElement = document.createElement('style'); + styleElement.id = 'autobot-auto-image-styles'; + styleElement.textContent = autoImageContent; + document.head.appendChild(styleElement); + + console.log(`%c✨ AUTO-INJECTED: auto-image-styles.css`, 'color: #10b981; font-weight: bold;'); + console.log(` 📏 Injected size: ${autoImageContent.length.toLocaleString()} characters`); + console.log(` 📍 Source: Extension local file`); + console.log(` 🎯 Target: as + `; + + const resizeContainer = document.createElement('div'); + resizeContainer.className = 'resize-container'; + resizeContainer.innerHTML = ` +

${Utils.t('resizeImage')}

+
+ + + + + +
+ + + + + + + 100% +
+ Drag to pan • Pinch to zoom • Double‑tap to zoom +
+
+
+ +
+
+
+ + +
+
+
+
+
+
+
+ +
+ + 1 +
+
+
+ +
+ + 1 +
+
+
+
+ +
+ + + +
+
+ + + Shift = Row • Alt = Column +
+
+ +
+
+  Color Palette +
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+  Advanced Color Matching +
+
+ + +
+
+ ${Utils.t('chromaWeight')} + ${state.chromaPenaltyWeight} +
+ +
+ +
+ + +
+ +
+
+ +
+ + + +
+ `; + + const resizeOverlay = document.createElement('div'); + resizeOverlay.className = 'resize-overlay'; + + document.body.appendChild(container); + document.body.appendChild(resizeOverlay); + document.body.appendChild(resizeContainer); + document.body.appendChild(statsContainer); + document.body.appendChild(settingsContainer); + + // Show the main container after all elements are appended + container.style.display = 'block'; + + const uploadBtn = container.querySelector('#uploadBtn'); + const resizeBtn = container.querySelector('#resizeBtn'); + const selectPosBtn = container.querySelector('#selectPosBtn'); + const startBtn = container.querySelector('#startBtn'); + const stopBtn = container.querySelector('#stopBtn'); + const saveBtn = container.querySelector('#saveBtn'); + const loadBtn = container.querySelector('#loadBtn'); + const saveToFileBtn = container.querySelector('#saveToFileBtn'); + const loadFromFileBtn = container.querySelector('#loadFromFileBtn'); + + container.querySelectorAll('.wplace-section-title').forEach((title) => { + // Add a right-side arrow if it doesn't exist + if (!title.querySelector('i.arrow')) { + const arrow = document.createElement('i'); + arrow.className = 'fas fa-chevron-down arrow'; // FontAwesome down arrow + title.appendChild(arrow); + } + + // Click event to toggle collapse/expand of the section + title.addEventListener('click', () => { + const section = title.parentElement; + section.classList.toggle('collapsed'); + }); + }); + + // Disable load/upload buttons until initial setup is complete (startup only) + if (loadBtn) { + loadBtn.disabled = !state.initialSetupComplete; + loadBtn.title = state.initialSetupComplete + ? '' + : '🔄 Waiting for initial setup to complete...'; + } + if (loadFromFileBtn) { + loadFromFileBtn.disabled = !state.initialSetupComplete; + loadFromFileBtn.title = state.initialSetupComplete + ? '' + : '🔄 Waiting for initial setup to complete...'; + } + if (uploadBtn) { + uploadBtn.disabled = !state.initialSetupComplete; + uploadBtn.title = state.initialSetupComplete + ? '' + : '🔄 Waiting for initial setup to complete...'; + } + + const minimizeBtn = container.querySelector('#minimizeBtn'); + const compactBtn = container.querySelector('#compactBtn'); + const statsBtn = container.querySelector('#statsBtn'); + const toggleOverlayBtn = container.querySelector('#toggleOverlayBtn'); + const statusText = container.querySelector('#statusText'); + const progressBar = container.querySelector('#progressBar'); + const statsArea = statsContainer.querySelector('#statsArea'); + const content = container.querySelector('.wplace-content'); + const closeStatsBtn = statsContainer.querySelector('#closeStatsBtn'); + const refreshChargesBtn = statsContainer.querySelector('#refreshChargesBtn'); + const cooldownSlider = container.querySelector('#cooldownSlider'); + const cooldownInput = container.querySelector('#cooldownInput'); + const cooldownDecrease = container.querySelector('#cooldownDecrease'); + const cooldownIncrease = container.querySelector('#cooldownIncrease'); + const cooldownValue = container.querySelector('#cooldownValue'); + + if (!uploadBtn || !selectPosBtn || !startBtn || !stopBtn) { + console.error('Some UI elements not found:', { + uploadBtn: !!uploadBtn, + selectPosBtn: !!selectPosBtn, + startBtn: !!startBtn, + stopBtn: !!stopBtn, + }); + } + + if (!statsContainer || !statsArea || !closeStatsBtn) { + // Note: base CSS now aligns with this layout: main panel at left:20px (width 280), stats at left:330px. + } + + const header = container.querySelector('.wplace-header'); + + makeDraggable(container); + + function makeDraggable(element) { + let pos1 = 0, + pos2 = 0, + pos3 = 0, + pos4 = 0; + let isDragging = false; + const header = + element.querySelector('.wplace-header') || element.querySelector('.wplace-settings-header'); + + if (!header) { + console.warn('No draggable header found for element:', element); + return; + } + + header.onmousedown = dragMouseDown; + + function dragMouseDown(e) { + if (e.target.closest('.wplace-header-btn') || e.target.closest('button')) return; + + e.preventDefault(); + isDragging = true; + + const rect = element.getBoundingClientRect(); + + element.style.transform = 'none'; + element.style.top = rect.top + 'px'; + element.style.left = rect.left + 'px'; + + pos3 = e.clientX; + pos4 = e.clientY; + element.classList.add('wplace-dragging'); + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + + document.body.style.userSelect = 'none'; + } + + function elementDrag(e) { + if (!isDragging) return; + + e.preventDefault(); + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + + let newTop = element.offsetTop - pos2; + let newLeft = element.offsetLeft - pos1; + + const rect = element.getBoundingClientRect(); + const maxTop = window.innerHeight - rect.height; + const maxLeft = window.innerWidth - rect.width; + + newTop = Math.max(0, Math.min(newTop, maxTop)); + newLeft = Math.max(0, Math.min(newLeft, maxLeft)); + + element.style.top = newTop + 'px'; + element.style.left = newLeft + 'px'; + } + + function closeDragElement() { + isDragging = false; + element.classList.remove('wplace-dragging'); + document.onmouseup = null; + document.onmousemove = null; + document.body.style.userSelect = ''; + } + } + + makeDraggable(statsContainer); + makeDraggable(container); + + if (statsBtn && closeStatsBtn) { + statsBtn.addEventListener('click', () => { + const isVisible = statsContainer.style.display !== 'none'; + if (isVisible) { + statsContainer.style.display = 'none'; + statsBtn.innerHTML = ''; + statsBtn.title = Utils.t('showStats'); + } else { + statsContainer.style.display = 'block'; + statsBtn.innerHTML = ''; + statsBtn.title = Utils.t('hideStats'); + } + }); + + closeStatsBtn.addEventListener('click', () => { + statsContainer.style.display = 'none'; + statsBtn.innerHTML = ''; + statsBtn.title = Utils.t('showStats'); + }); + + if (refreshChargesBtn) { + refreshChargesBtn.addEventListener('click', async () => { + refreshChargesBtn.innerHTML = ''; + refreshChargesBtn.disabled = true; + + try { + await updateStats(true); + } catch (error) { + console.error('Error refreshing charges:', error); + } finally { + refreshChargesBtn.innerHTML = ''; + refreshChargesBtn.disabled = false; + } + }); + } + } + if (statsContainer && statsBtn) { + // Stats container starts hidden - user clicks button to show + statsBtn.innerHTML = ''; + statsBtn.title = Utils.t('showStats'); + } + + const settingsBtn = container.querySelector('#settingsBtn'); + const closeSettingsBtn = settingsContainer.querySelector('#closeSettingsBtn'); + const applySettingsBtn = settingsContainer.querySelector('#applySettingsBtn'); + + if (settingsBtn && closeSettingsBtn && applySettingsBtn) { + settingsBtn.addEventListener('click', () => { + const isVisible = settingsContainer.classList.contains('show'); + if (isVisible) { + settingsContainer.style.animation = 'settings-fade-out 0.3s ease-out forwards'; + settingsContainer.classList.remove('show'); + setTimeout(() => { + settingsContainer.style.animation = ''; + }, 300); + } else { + settingsContainer.style.top = '50%'; + settingsContainer.style.left = '50%'; + settingsContainer.style.transform = 'translate(-50%, -50%)'; + settingsContainer.classList.add('show'); + settingsContainer.style.animation = 'settings-slide-in 0.4s ease-out'; + } + }); + + closeSettingsBtn.addEventListener('click', () => { + settingsContainer.style.animation = 'settings-fade-out 0.3s ease-out forwards'; + settingsContainer.classList.remove('show'); + setTimeout(() => { + settingsContainer.style.animation = ''; + settingsContainer.style.top = '50%'; + settingsContainer.style.left = '50%'; + settingsContainer.style.transform = 'translate(-50%, -50%)'; + }, 300); + }); + + applySettingsBtn.addEventListener('click', () => { + // Sync advanced settings before save + const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); + if (colorAlgorithmSelect) state.colorMatchingAlgorithm = colorAlgorithmSelect.value; + const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); + if (enableChromaPenaltyToggle) + state.enableChromaPenalty = enableChromaPenaltyToggle.checked; + const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); + if (chromaPenaltyWeightSlider) + state.chromaPenaltyWeight = parseFloat(chromaPenaltyWeightSlider.value) || 0.15; + const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); + if (transparencyThresholdInput) { + const v = parseInt(transparencyThresholdInput.value, 10); + if (!isNaN(v) && v >= 0 && v <= 255) state.customTransparencyThreshold = v; + } + const whiteThresholdInput = document.getElementById('whiteThresholdInput'); + if (whiteThresholdInput) { + const v = parseInt(whiteThresholdInput.value, 10); + if (!isNaN(v) && v >= 200 && v <= 255) state.customWhiteThreshold = v; + } + // Update functional thresholds + CONFIG.TRANSPARENCY_THRESHOLD = state.customTransparencyThreshold; + CONFIG.WHITE_THRESHOLD = state.customWhiteThreshold; + // Notifications + const notifEnabledToggle = document.getElementById('notifEnabledToggle'); + const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); + const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); + const notifIntervalInput = document.getElementById('notifIntervalInput'); + if (notifEnabledToggle) state.notificationsEnabled = !!notifEnabledToggle.checked; + if (notifOnChargesToggle) state.notifyOnChargesReached = !!notifOnChargesToggle.checked; + if (notifOnlyUnfocusedToggle) + state.notifyOnlyWhenUnfocused = !!notifOnlyUnfocusedToggle.checked; + if (notifIntervalInput) { + const v = parseInt(notifIntervalInput.value, 10); + if (!isNaN(v) && v >= 1 && v <= 60) state.notificationIntervalMinutes = v; + } + saveBotSettings(); + Utils.showAlert(Utils.t('settingsSaved'), 'success'); + closeSettingsBtn.click(); + NotificationManager.syncFromState(); + }); + + makeDraggable(settingsContainer); + + const tokenSourceSelect = settingsContainer.querySelector('#tokenSourceSelect'); + if (tokenSourceSelect) { + tokenSourceSelect.addEventListener('change', (e) => { + state.tokenSource = e.target.value; + saveBotSettings(); + console.log(`🔑 Token source changed to: ${state.tokenSource}`); + const sourceNames = { + generator: 'Automatic Generator', + hybrid: 'Generator + Auto Fallback', + manual: 'Manual Pixel Placement', + }; + Utils.showAlert( + Utils.t('tokenSourceSet', { source: sourceNames[state.tokenSource] }), + 'success' + ); + }); + } + + // Batch mode controls + const batchModeSelect = settingsContainer.querySelector('#batchModeSelect'); + const normalBatchControls = settingsContainer.querySelector('#normalBatchControls'); + const randomBatchControls = settingsContainer.querySelector('#randomBatchControls'); + const randomBatchMin = settingsContainer.querySelector('#randomBatchMin'); + const randomBatchMax = settingsContainer.querySelector('#randomBatchMax'); + + if (batchModeSelect) { + batchModeSelect.addEventListener('change', (e) => { + state.batchMode = e.target.value; + + // Switch between normal and random controls + if (normalBatchControls && randomBatchControls) { + if (e.target.value === 'random') { + normalBatchControls.style.display = 'none'; + randomBatchControls.style.display = 'block'; + } else { + normalBatchControls.style.display = 'block'; + randomBatchControls.style.display = 'none'; + } + } + + saveBotSettings(); + console.log(`📦 Batch mode changed to: ${state.batchMode}`); + Utils.showAlert( + Utils.t('batchModeSet', { + mode: + state.batchMode === 'random' ? Utils.t('randomRange') : Utils.t('normalFixedSize'), + }), + 'success' + ); + }); + } + + if (randomBatchMin) { + randomBatchMin.addEventListener('input', (e) => { + const min = parseInt(e.target.value); + if (min >= 1 && min <= 1000) { + state.randomBatchMin = min; + // Ensure min doesn't exceed max + if (randomBatchMax && min > state.randomBatchMax) { + state.randomBatchMax = min; + randomBatchMax.value = min; + } + saveBotSettings(); + } + }); + } + + if (randomBatchMax) { + randomBatchMax.addEventListener('input', (e) => { + const max = parseInt(e.target.value); + if (max >= 1 && max <= 1000) { + state.randomBatchMax = max; + // Ensure max doesn't go below min + if (randomBatchMin && max < state.randomBatchMin) { + state.randomBatchMin = max; + randomBatchMin.value = max; + } + saveBotSettings(); + } + }); + } + + const languageSelect = settingsContainer.querySelector('#languageSelect'); + if (languageSelect) { + languageSelect.addEventListener('change', async (e) => { + const newLanguage = e.target.value; + state.language = newLanguage; + localStorage.setItem('wplace_language', newLanguage); + + // Load the new language translations + await loadTranslations(newLanguage); + + setTimeout(() => { + settingsContainer.style.display = 'none'; + createUI(); + }, 100); + }); + } + + const themeSelect = settingsContainer.querySelector('#themeSelect'); + if (themeSelect) { + themeSelect.addEventListener('change', (e) => { + const newTheme = e.target.value; + switchTheme(newTheme); + }); + } + + const overlayOpacitySlider = settingsContainer.querySelector('#overlayOpacitySlider'); + const overlayOpacityValue = settingsContainer.querySelector('#overlayOpacityValue'); + const enableBlueMarbleToggle = settingsContainer.querySelector('#enableBlueMarbleToggle'); + const settingsPaintWhiteToggle = settingsContainer.querySelector('#settingsPaintWhiteToggle'); + const settingsPaintTransparentToggle = settingsContainer.querySelector( + '#settingsPaintTransparentToggle' + ); + + if (overlayOpacitySlider && overlayOpacityValue) { + const updateOpacity = (newValue) => { + const opacity = parseFloat(newValue); + state.overlayOpacity = opacity; + overlayOpacitySlider.value = opacity; + overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`; + }; + + overlayOpacitySlider.addEventListener('input', (e) => { + updateOpacity(e.target.value); + }); + + // Add scroll-to-adjust for overlay opacity slider + Utils.createScrollToAdjust(overlayOpacitySlider, updateOpacity, 0, 1, 0.05); + } + + if (settingsPaintWhiteToggle) { + settingsPaintWhiteToggle.checked = state.paintWhitePixels; + settingsPaintWhiteToggle.addEventListener('change', (e) => { + state.paintWhitePixels = e.target.checked; + saveBotSettings(); + console.log(`🎨 Paint white pixels: ${state.paintWhitePixels ? 'ON' : 'OFF'}`); + const statusText = state.paintWhitePixels + ? 'White pixels in the template will be painted' + : 'White pixels will be skipped'; + Utils.showAlert(statusText, 'success'); + }); + } + + if (settingsPaintTransparentToggle) { + settingsPaintTransparentToggle.checked = state.paintTransparentPixels; + settingsPaintTransparentToggle.addEventListener('change', (e) => { + state.paintTransparentPixels = e.target.checked; + saveBotSettings(); + console.log( + `🎨 Paint transparent pixels: ${state.paintTransparentPixels ? 'ON' : 'OFF'}` + ); + const statusText = state.paintTransparentPixels + ? 'Transparent pixels in the template will be painted with the closest available color' + : 'Transparent pixels will be skipped'; + Utils.showAlert(statusText, 'success'); + }); + } + + // Speed controls - both slider and input + const speedSlider = settingsContainer.querySelector('#speedSlider'); + const speedInput = settingsContainer.querySelector('#speedInput'); + const speedDecrease = settingsContainer.querySelector('#speedDecrease'); + const speedIncrease = settingsContainer.querySelector('#speedIncrease'); + const speedValue = settingsContainer.querySelector('#speedValue'); + + if (speedSlider && speedInput && speedValue && speedDecrease && speedIncrease) { + const updateSpeed = (newValue) => { + const speed = Math.max(CONFIG.PAINTING_SPEED.MIN, Math.min(CONFIG.PAINTING_SPEED.MAX, parseInt(newValue))); + state.paintingSpeed = speed; + + // Update both controls (value shows in input, label shows unit only) + speedSlider.value = speed; + speedInput.value = speed; + speedValue.textContent = `pixels`; + + saveBotSettings(); + }; + + // Slider event listener + speedSlider.addEventListener('input', (e) => { + updateSpeed(e.target.value); + }); + + // Number input event listener + speedInput.addEventListener('input', (e) => { + updateSpeed(e.target.value); + }); + + // Decrease button + speedDecrease.addEventListener('click', () => { + updateSpeed(parseInt(speedInput.value) - 1); + }); + + // Increase button + speedIncrease.addEventListener('click', () => { + updateSpeed(parseInt(speedInput.value) + 1); + }); + + // Add scroll-to-adjust for speed slider + Utils.createScrollToAdjust(speedSlider, updateSpeed, CONFIG.PAINTING_SPEED.MIN, CONFIG.PAINTING_SPEED.MAX, 1); + } + + if (enableBlueMarbleToggle) { + enableBlueMarbleToggle.addEventListener('click', async () => { + state.blueMarbleEnabled = enableBlueMarbleToggle.checked; + if (state.imageLoaded && overlayManager.imageBitmap) { + Utils.showAlert(Utils.t('reprocessingOverlay'), 'info'); + await overlayManager.processImageIntoChunks(); + Utils.showAlert(Utils.t('overlayUpdated'), 'success'); + } + }); + } + + // (Advanced color listeners moved outside to work with resize dialog) + // (Advanced color listeners moved outside to work with resize dialog) + // Notifications listeners + const notifPermBtn = settingsContainer.querySelector('#notifRequestPermBtn'); + const notifTestBtn = settingsContainer.querySelector('#notifTestBtn'); + if (notifPermBtn) { + notifPermBtn.addEventListener('click', async () => { + const perm = await NotificationManager.requestPermission(); + if (perm === 'granted') Utils.showAlert(Utils.t('notificationsEnabled'), 'success'); + else Utils.showAlert(Utils.t('notificationsPermissionDenied'), 'warning'); + }); + } + if (notifTestBtn) { + notifTestBtn.addEventListener('click', () => { + NotificationManager.notify( + Utils.t('testNotificationTitle'), + Utils.t('testNotificationMessage'), + 'wplace-notify-test', + true + ); + }); + } + } + + const widthSlider = resizeContainer.querySelector('#widthSlider'); + const heightSlider = resizeContainer.querySelector('#heightSlider'); + const widthValue = resizeContainer.querySelector('#widthValue'); + const heightValue = resizeContainer.querySelector('#heightValue'); + const keepAspect = resizeContainer.querySelector('#keepAspect'); + const paintWhiteToggle = resizeContainer.querySelector('#paintWhiteToggle'); + const paintTransparentToggle = resizeContainer.querySelector('#paintTransparentToggle'); + const zoomSlider = resizeContainer.querySelector('#zoomSlider'); + const zoomValue = resizeContainer.querySelector('#zoomValue'); + const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); + const zoomOutBtn = resizeContainer.querySelector('#zoomOutBtn'); + const zoomFitBtn = resizeContainer.querySelector('#zoomFitBtn'); + const zoomActualBtn = resizeContainer.querySelector('#zoomActualBtn'); + const panModeBtn = resizeContainer.querySelector('#panModeBtn'); + const panStage = resizeContainer.querySelector('#resizePanStage'); + const canvasStack = resizeContainer.querySelector('#resizeCanvasStack'); + const baseCanvas = resizeContainer.querySelector('#resizeCanvas'); + const maskCanvas = resizeContainer.querySelector('#maskCanvas'); + const baseCtx = baseCanvas.getContext('2d'); + const maskCtx = maskCanvas.getContext('2d'); + const confirmResize = resizeContainer.querySelector('#confirmResize'); + const cancelResize = resizeContainer.querySelector('#cancelResize'); + const downloadPreviewBtn = resizeContainer.querySelector('#downloadPreviewBtn'); + const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn'); + + // Coordinate generation controls with smart visibility + const coordinateModeSelect = settingsContainer.querySelector('#coordinateModeSelect'); + const coordinateDirectionSelect = settingsContainer.querySelector('#coordinateDirectionSelect'); + const coordinateSnakeToggle = settingsContainer.querySelector('#coordinateSnakeToggle'); + const directionControls = settingsContainer.querySelector('#directionControls'); + const snakeControls = settingsContainer.querySelector('#snakeControls'); + const blockControls = settingsContainer.querySelector('#blockControls'); + const blockWidthInput = settingsContainer.querySelector('#blockWidthInput'); + const blockHeightInput = settingsContainer.querySelector('#blockHeightInput'); + const paintUnavailablePixelsToggle = settingsContainer.querySelector( + '#paintUnavailablePixelsToggle' + ); + + if (paintUnavailablePixelsToggle) { + paintUnavailablePixelsToggle.checked = state.paintUnavailablePixels; + paintUnavailablePixelsToggle.addEventListener('change', (e) => { + state.paintUnavailablePixels = e.target.checked; + saveBotSettings(); + console.log(`🎨 Paint unavailable colors: ${state.paintUnavailablePixels ? 'ON' : 'OFF'}`); + const statusText = state.paintUnavailablePixels + ? 'Unavailable template colors will be painted with the closest available color' + : 'Unavailable template colors will be skipped'; + Utils.showAlert(statusText, 'success'); + }); + } + if (coordinateModeSelect) { + coordinateModeSelect.value = state.coordinateMode; + coordinateModeSelect.addEventListener('change', (e) => { + state.coordinateMode = e.target.value; + Utils.updateCoordinateUI({ + mode: state.coordinateMode, + directionControls, + snakeControls, + blockControls, + }); + saveBotSettings(); + console.log(`🔄 Coordinate mode changed to: ${state.coordinateMode}`); + Utils.showAlert(`Coordinate mode set to: ${state.coordinateMode}`, 'success'); + }); + } + + if (coordinateDirectionSelect) { + coordinateDirectionSelect.value = state.coordinateDirection; + coordinateDirectionSelect.addEventListener('change', (e) => { + state.coordinateDirection = e.target.value; + saveBotSettings(); + console.log(`🧭 Coordinate direction changed to: ${state.coordinateDirection}`); + Utils.showAlert(`Coordinate direction set to: ${state.coordinateDirection}`, 'success'); + }); + } + + if (coordinateSnakeToggle) { + coordinateSnakeToggle.checked = state.coordinateSnake; + coordinateSnakeToggle.addEventListener('change', (e) => { + state.coordinateSnake = e.target.checked; + saveBotSettings(); + console.log(`🐍 Snake pattern ${state.coordinateSnake ? 'enabled' : 'disabled'}`); + Utils.showAlert( + `Snake pattern ${state.coordinateSnake ? 'enabled' : 'disabled'}`, + 'success' + ); + }); + } + + if (blockWidthInput) { + blockWidthInput.value = state.blockWidth; + blockWidthInput.addEventListener('input', (e) => { + const width = parseInt(e.target.value); + if (width >= 1 && width <= 50) { + state.blockWidth = width; + saveBotSettings(); + } + }); + } + + if (blockHeightInput) { + blockHeightInput.value = state.blockHeight; + blockHeightInput.addEventListener('change', (e) => { + const height = parseInt(e.target.value); + if (height >= 1 && height <= 50) { + state.blockHeight = height; + saveBotSettings(); + } + }); + } + + if (compactBtn) { + compactBtn.addEventListener('click', () => { + container.classList.toggle('wplace-compact'); + const isCompact = container.classList.contains('wplace-compact'); + + if (isCompact) { + compactBtn.innerHTML = ''; + compactBtn.title = Utils.t('expandMode'); + } else { + compactBtn.innerHTML = ''; + compactBtn.title = Utils.t('compactMode'); + } + }); + } + + if (minimizeBtn) { + minimizeBtn.addEventListener('click', () => { + state.minimized = !state.minimized; + if (state.minimized) { + container.classList.add('wplace-minimized'); + content.classList.add('wplace-hidden'); + minimizeBtn.innerHTML = ''; + minimizeBtn.title = Utils.t('restore'); + } else { + container.classList.remove('wplace-minimized'); + content.classList.remove('wplace-hidden'); + minimizeBtn.innerHTML = ''; + minimizeBtn.title = Utils.t('minimize'); + } + saveBotSettings(); + }); + } + + if (toggleOverlayBtn) { + toggleOverlayBtn.addEventListener('click', () => { + const isEnabled = overlayManager.toggle(); + toggleOverlayBtn.classList.toggle('active', isEnabled); + toggleOverlayBtn.setAttribute('aria-pressed', isEnabled ? 'true' : 'false'); + Utils.showAlert(isEnabled ? Utils.t('overlayEnabled') : Utils.t('overlayDisabled'), 'info'); + }); + } + + if (state.minimized) { + container.classList.add('wplace-minimized'); + content.classList.add('wplace-hidden'); + if (minimizeBtn) { + minimizeBtn.innerHTML = ''; + minimizeBtn.title = Utils.t('restore'); + } + } else { + container.classList.remove('wplace-minimized'); + content.classList.remove('wplace-hidden'); + if (minimizeBtn) { + minimizeBtn.innerHTML = ''; + minimizeBtn.title = Utils.t('minimize'); + } + } + + if (saveBtn) { + saveBtn.addEventListener('click', () => { + if (!state.imageLoaded) { + Utils.showAlert(Utils.t('missingRequirements'), 'error'); + return; + } + + const success = Utils.saveProgress(); + if (success) { + updateUI('autoSaved', 'success'); + Utils.showAlert(Utils.t('autoSaved'), 'success'); + } else { + Utils.showAlert(Utils.t('errorSavingProgress'), 'error'); + } + }); + } + + if (loadBtn) { + loadBtn.addEventListener('click', () => { + // Check if initial setup is complete + if (!state.initialSetupComplete) { + Utils.showAlert(Utils.t('pleaseWaitInitialSetup'), 'warning'); + return; + } + + const savedData = Utils.loadProgress(); + if (!savedData) { + updateUI('noSavedData', 'warning'); + Utils.showAlert(Utils.t('noSavedData'), 'warning'); + return; + } + + const confirmLoad = confirm( + `${Utils.t('savedDataFound')}\n\n` + + `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels` + ); + + if (confirmLoad) { + const success = Utils.restoreProgress(savedData); + if (success) { + updateUI('dataLoaded', 'success'); + Utils.showAlert(Utils.t('dataLoaded'), 'success'); + updateDataButtons(); + + updateStats(); + + // Restore overlay if image data was loaded from localStorage + Utils.restoreOverlayFromData().catch((error) => { + console.error('Failed to restore overlay from localStorage:', error); + }); + + if (!state.colorsChecked) { + uploadBtn.disabled = false; + } else { + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + } + + if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { + startBtn.disabled = false; + } + } else { + Utils.showAlert(Utils.t('errorLoadingProgress'), 'error'); + } + } + }); + } + + if (saveToFileBtn) { + saveToFileBtn.addEventListener('click', () => { + const success = Utils.saveProgressToFile(); + if (success) { + updateUI('fileSaved', 'success'); + Utils.showAlert(Utils.t('fileSaved'), 'success'); + } else { + Utils.showAlert(Utils.t('fileError'), 'error'); + } + }); + } + + if (loadFromFileBtn) { + loadFromFileBtn.addEventListener('click', async () => { + // Check if initial setup is complete + if (!state.initialSetupComplete) { + Utils.showAlert(Utils.t('pleaseWaitFileSetup'), 'warning'); + return; + } + + try { + const success = await Utils.loadProgressFromFile(); + if (success) { + updateUI('fileLoaded', 'success'); + Utils.showAlert(Utils.t('fileLoaded'), 'success'); + updateDataButtons(); + + await updateStats(); + + // Restore overlay if image data was loaded from file + await Utils.restoreOverlayFromData().catch((error) => { + console.error('Failed to restore overlay from file:', error); + }); + + if (state.colorsChecked) { + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + resizeBtn.disabled = false; + } else { + uploadBtn.disabled = false; + } + + if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { + startBtn.disabled = false; + } + } + } catch (error) { + if (error.message === 'Invalid JSON file') { + Utils.showAlert(Utils.t('invalidFileFormat'), 'error'); + } else { + Utils.showAlert(Utils.t('fileError'), 'error'); + } + } + }); + } + + updateUI = (messageKey, type = 'default', params = {}, silent = false) => { + const message = Utils.t(messageKey, params); + statusText.textContent = message; + statusText.className = `wplace-status status-${type}`; + + if (!silent) { + // Trigger animation only when silent = false + statusText.style.animation = 'none'; + void statusText.offsetWidth; // trick to restart the animation + statusText.style.animation = 'slide-in 0.3s ease-out'; + } + }; + + function updateChargeStatsDisplay(intervalMs) { + const currentChargesEl = document.getElementById('wplace-stat-charges-value'); + const fullChargeEl = document.getElementById('wplace-stat-fullcharge-value'); + if (!fullChargeEl && !currentChargesEl) return; + if (!state.fullChargeData) { + fullChargeEl.textContent = '--:--:--'; + return; + } + + const { current, max, cooldownMs, startTime, spentSinceShot } = state.fullChargeData; + const elapsed = Date.now() - startTime; + + // total charges including elapsed time and spent during painting since snapshot + const chargesGained = elapsed / cooldownMs; + const rawCharges = current + chargesGained - spentSinceShot; + const cappedCharges = Math.min(rawCharges, max); + + // rounding with 0.95 threshold + let displayCharges; + const fraction = cappedCharges - Math.floor(cappedCharges); + if (fraction >= 0.95) { + displayCharges = Math.ceil(cappedCharges); + } else { + displayCharges = Math.floor(cappedCharges); + } + + state.displayCharges = Math.max(0, displayCharges); + state.preciseCurrentCharges = cappedCharges; + + const remainingMs = getMsToTargetCharges(cappedCharges, max, state.cooldown, intervalMs); + const timeText = Utils.msToTimeText(remainingMs); + + if (currentChargesEl) { + currentChargesEl.innerHTML = `${state.displayCharges} / ${state.maxCharges}`; + } + + if ( + state.displayCharges < state.cooldownChargeThreshold && + !state.stopFlag && + state.running + ) { + updateChargesThresholdUI(intervalMs); + } + + if (fullChargeEl) { + if (state.displayCharges >= max) { + fullChargeEl.innerHTML = `FULL`; + } else { + fullChargeEl.innerHTML = ` + ${timeText} + `; + } + } + } + + updateStats = async (isManualRefresh = false) => { + const isForcedRefresh = isManualRefresh; + const isFirstCheck = !state.fullChargeData?.startTime; + + const minUpdateInterval = 60_000; + const maxUpdateInterval = 90_000; + const randomUpdateThreshold = + minUpdateInterval + Math.random() * (maxUpdateInterval - minUpdateInterval); + const timeSinceLastUpdate = Date.now() - (state.fullChargeData?.startTime || 0); + const isTimeToUpdate = timeSinceLastUpdate >= randomUpdateThreshold; + + const shouldCallApi = isForcedRefresh || isFirstCheck || isTimeToUpdate; + + if (shouldCallApi) { + const { charges, max, cooldown } = await WPlaceService.getCharges(); + state.displayCharges = Math.floor(charges); + state.preciseCurrentCharges = charges; + state.cooldown = cooldown; + state.maxCharges = Math.floor(max) > 1 ? Math.floor(max) : state.maxCharges; + + state.fullChargeData = { + current: charges, + max: max, + cooldownMs: cooldown, + startTime: Date.now(), + spentSinceShot: 0, + }; + // Evaluate notifications every time we refresh server-side charges + NotificationManager.maybeNotifyChargesReached(); + } + + if (state.fullChargeInterval) { + clearInterval(state.fullChargeInterval); + state.fullChargeInterval = null; + } + const intervalMs = 1000; + state.fullChargeInterval = setInterval( + () => updateChargeStatsDisplay(intervalMs), + intervalMs + ); + + if (cooldownSlider && cooldownSlider.max !== state.maxCharges) { + cooldownSlider.max = state.maxCharges; + } + if (cooldownInput && cooldownInput.max !== state.maxCharges) { + cooldownInput.max = state.maxCharges; + } + + let imageStatsHTML = ''; + if (state.imageLoaded) { + const progress = + state.totalPixels > 0 ? Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; + const remainingPixels = state.totalPixels - state.paintedPixels; + state.estimatedTime = Utils.calculateEstimatedTime( + remainingPixels, + state.displayCharges, + state.cooldown + ); + progressBar.style.width = `${progress}%`; + + imageStatsHTML = ` +
+
${Utils.t('progress')}
+
${progress}%
+
+
+
${Utils.t( + 'pixels' + )}
+
${state.paintedPixels}/${state.totalPixels}
+
+
+
${Utils.t( + 'estimatedTime' + )}
+
${Utils.formatTime(state.estimatedTime)}
+
+ `; + } + + let colorSwatchesHTML = ''; + state.availableColors = state.availableColors.filter( + (c) => c.name !== 'Unknown CoIor NaN' && c.id !== null + ); + + const availableColors = Utils.extractAvailableColors(); + const newCount = Array.isArray(availableColors) ? availableColors.length : 0; + + if (newCount === 0 && isManualRefresh) { + Utils.showAlert(Utils.t('noColorsFound'), 'warning'); + } else if (newCount > 0 && state.availableColors.length < newCount) { + const oldCount = state.availableColors.length; + + Utils.showAlert( + Utils.t('colorsUpdated', { + oldCount, + newCount: newCount, + diffCount: newCount - oldCount, + }), + 'success' + ); + state.availableColors = availableColors; + } + if (state.colorsChecked) { + colorSwatchesHTML = state.availableColors + .map((color) => { + const rgbString = `rgb(${color.rgb.join(',')})`; + return `
`; + }) + .join(''); + } + + statsArea.innerHTML = ` + ${imageStatsHTML} +
+
+ ${Utils.t('charges')} +
+
+ ${state.displayCharges} / ${state.maxCharges} +
+
+
+
+ ${Utils.t('fullChargeIn')} +
+
--:--:--
+
+ ${state.colorsChecked + ? ` +
+
${Utils.t( + 'availableColors', + { count: state.availableColors.length } + )}
+
+ ${colorSwatchesHTML} +
+
+ ` + : '' + } + `; + + // should be after statsArea.innerHTML = '...'. todo make full stats ui update partial + updateChargeStatsDisplay(intervalMs); + }; + + updateDataButtons = () => { + const hasImageData = state.imageLoaded && state.imageData; + saveBtn.disabled = !hasImageData; + saveToFileBtn.disabled = !hasImageData; + }; + + updateDataButtons(); + + function showResizeDialog(processor) { + let baseProcessor = processor; + let width, height; + if (state.originalImage?.dataUrl) { + baseProcessor = new ImageProcessor(state.originalImage.dataUrl); + width = state.originalImage.width; + height = state.originalImage.height; + } else { + const dims = processor.getDimensions(); + width = dims.width; + height = dims.height; + } + const aspectRatio = width / height; + + const rs = state.resizeSettings; + widthSlider.max = width * 2; + heightSlider.max = height * 2; + let initialW = width; + let initialH = height; + if ( + rs && + Number.isFinite(rs.width) && + Number.isFinite(rs.height) && + rs.width > 0 && + rs.height > 0 + ) { + initialW = rs.width; + initialH = rs.height; + } + // Clamp to slider ranges + initialW = Math.max( + parseInt(widthSlider.min, 10) || 10, + Math.min(initialW, parseInt(widthSlider.max, 10)) + ); + initialH = Math.max( + parseInt(heightSlider.min, 10) || 10, + Math.min(initialH, parseInt(heightSlider.max, 10)) + ); + widthSlider.value = initialW; + heightSlider.value = initialH; + widthValue.textContent = initialW; + heightValue.textContent = initialH; + zoomSlider.value = 1; + if (zoomValue) zoomValue.textContent = '100%'; + paintWhiteToggle.checked = state.paintWhitePixels; + paintTransparentToggle.checked = state.paintTransparentPixels; + + let _previewTimer = null; + let _previewJobId = 0; + let _isDraggingSize = false; + let _zoomLevel = 1; + let _ditherWorkBuf = null; + let _ditherEligibleBuf = null; + const ensureDitherBuffers = (n) => { + if (!_ditherWorkBuf || _ditherWorkBuf.length !== n * 3) + _ditherWorkBuf = new Float32Array(n * 3); + if (!_ditherEligibleBuf || _ditherEligibleBuf.length !== n) + _ditherEligibleBuf = new Uint8Array(n); + return { work: _ditherWorkBuf, eligible: _ditherEligibleBuf }; + }; + let _maskImageData = null; + let _maskData = null; + let _dirty = null; + const _resetDirty = () => { + _dirty = { minX: Infinity, minY: Infinity, maxX: -1, maxY: -1 }; + }; + const _markDirty = (x, y) => { + if (!_dirty) _resetDirty(); + if (x < _dirty.minX) _dirty.minX = x; + if (y < _dirty.minY) _dirty.minY = y; + if (x > _dirty.maxX) _dirty.maxX = x; + if (y > _dirty.maxY) _dirty.maxY = y; + }; + const _flushDirty = () => { + if (!_dirty || _dirty.maxX < _dirty.minX || _dirty.maxY < _dirty.minY) return; + const x = Math.max(0, _dirty.minX); + const y = Math.max(0, _dirty.minY); + const w = Math.min(maskCanvas.width - x, _dirty.maxX - x + 1); + const h = Math.min(maskCanvas.height - y, _dirty.maxY - y + 1); + if (w > 0 && h > 0) maskCtx.putImageData(_maskImageData, 0, 0, x, y, w, h); + _resetDirty(); + }; + const _ensureMaskOverlayBuffers = (w, h, rebuildFromMask = false) => { + if (!_maskImageData || _maskImageData.width !== w || _maskImageData.height !== h) { + _maskImageData = maskCtx.createImageData(w, h); + _maskData = _maskImageData.data; + rebuildFromMask = true; + } + if (rebuildFromMask) { + const m = state.resizeIgnoreMask; + const md = _maskData; + md.fill(0); + if (m) { + for (let i = 0; i < m.length; i++) + if (m[i]) { + const p = i * 4; + md[p] = 255; + md[p + 1] = 0; + md[p + 2] = 0; + md[p + 3] = 150; + } + } + maskCtx.putImageData(_maskImageData, 0, 0); + _resetDirty(); + } + }; + const ensureMaskSize = (w, h) => { + if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { + state.resizeIgnoreMask = new Uint8Array(w * h); + } + baseCanvas.width = w; + baseCanvas.height = h; + maskCanvas.width = w; + maskCanvas.height = h; + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + // Ensure overlay buffers exist and rebuild from mask when dimensions change + _ensureMaskOverlayBuffers(w, h, true); + }; + _updateResizePreview = async () => { + const jobId = ++_previewJobId; + const newWidth = parseInt(widthSlider.value, 10); + const newHeight = parseInt(heightSlider.value, 10); + _zoomLevel = parseFloat(zoomSlider.value); + + widthValue.textContent = newWidth; + heightValue.textContent = newHeight; + + ensureMaskSize(newWidth, newHeight); + canvasStack.style.width = newWidth + 'px'; + canvasStack.style.height = newHeight + 'px'; + baseCtx.imageSmoothingEnabled = false; + if (!state.availableColors || state.availableColors.length === 0) { + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + baseCtx.clearRect(0, 0, newWidth, newHeight); + baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + // Draw existing mask overlay buffer + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); + updateZoomLayout(); + return; + } + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + baseCtx.clearRect(0, 0, newWidth, newHeight); + baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + const imgData = baseCtx.getImageData(0, 0, newWidth, newHeight); + const data = imgData.data; + + const tThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + + const applyFSDither = () => { + const w = newWidth, + h = newHeight; + const n = w * h; + const { work, eligible } = ensureDitherBuffers(n); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + const i4 = idx * 4; + const r = data[i4], + g = data[i4 + 1], + b = data[i4 + 2], + a = data[i4 + 3]; + const isEligible = + (state.paintTransparentPixels || a >= tThresh) && + (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + eligible[idx] = isEligible ? 1 : 0; + work[idx * 3] = r; + work[idx * 3 + 1] = g; + work[idx * 3 + 2] = b; + if (!isEligible) { + data[i4 + 3] = 0; // transparent in preview overlay + } + } + } + + const diffuse = (nx, ny, er, eg, eb, factor) => { + if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; + const nidx = ny * w + nx; + if (!eligible[nidx]) return; + const base = nidx * 3; + work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); + work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); + work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); + }; + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + if (!eligible[idx]) continue; + const base = idx * 3; + const r0 = work[base], + g0 = work[base + 1], + b0 = work[base + 2]; + const [nr, ng, nb] = Utils.findClosestPaletteColor( + r0, + g0, + b0, + state.activeColorPalette + ); + const i4 = idx * 4; + data[i4] = nr; + data[i4 + 1] = ng; + data[i4 + 2] = nb; + data[i4 + 3] = 255; + + const er = r0 - nr; + const eg = g0 - ng; + const eb = b0 - nb; + + diffuse(x + 1, y, er, eg, eb, 7 / 16); + diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); + diffuse(x, y + 1, er, eg, eb, 5 / 16); + diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); + } + } + }; + + // Skip expensive dithering while user is dragging sliders + if (state.ditheringEnabled && !_isDraggingSize) { + applyFSDither(); + } else { + for (let i = 0; i < data.length; i += 4) { + const r = data[i], + g = data[i + 1], + b = data[i + 2], + a = data[i + 3]; + if ( + (!state.paintTransparentPixels && a < tThresh) || + (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) + ) { + data[i + 3] = 0; + continue; + } + const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + data[i] = nr; + data[i + 1] = ng; + data[i + 2] = nb; + data[i + 3] = 255; + } + } + + if (jobId !== _previewJobId) return; + baseCtx.putImageData(imgData, 0, 0); + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); + updateZoomLayout(); + }; + + const onWidthInput = () => { + if (keepAspect.checked) { + heightSlider.value = Math.round(parseInt(widthSlider.value, 10) / aspectRatio); + } + _updateResizePreview(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { + baseWidth: width, + baseHeight: height, + width: curW, + height: curH, + }; + saveBotSettings(); + // Auto-fit after size changes + const fit = typeof computeFitZoom === 'function' ? computeFitZoom() : 1; + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + }; + + const onHeightInput = () => { + if (keepAspect.checked) { + widthSlider.value = Math.round(parseInt(heightSlider.value, 10) * aspectRatio); + } + _updateResizePreview(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { + baseWidth: width, + baseHeight: height, + width: curW, + height: curH, + }; + saveBotSettings(); + // Auto-fit after size changes + const fit = typeof computeFitZoom === 'function' ? computeFitZoom() : 1; + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + }; + + paintWhiteToggle.onchange = (e) => { + state.paintWhitePixels = e.target.checked; + _updateResizePreview(); + saveBotSettings(); + }; + + paintTransparentToggle.onchange = (e) => { + state.paintTransparentPixels = e.target.checked; + _updateResizePreview(); + saveBotSettings(); + }; + + let panX = 0, + panY = 0; + const clampPan = () => { + const wrapRect = panStage?.getBoundingClientRect() || { + width: 0, + height: 0, + }; + const w = (baseCanvas.width || 1) * _zoomLevel; + const h = (baseCanvas.height || 1) * _zoomLevel; + if (w <= wrapRect.width) { + panX = Math.floor((wrapRect.width - w) / 2); + } else { + const minX = wrapRect.width - w; + panX = Math.min(0, Math.max(minX, panX)); + } + if (h <= wrapRect.height) { + panY = Math.floor((wrapRect.height - h) / 2); + } else { + const minY = wrapRect.height - h; + panY = Math.min(0, Math.max(minY, panY)); + } + }; + let _panRaf = 0; + const applyPan = () => { + if (_panRaf) return; + _panRaf = requestAnimationFrame(() => { + clampPan(); + canvasStack.style.transform = `translate3d(${Math.round( + panX + )}px, ${Math.round(panY)}px, 0) scale(${_zoomLevel})`; + _panRaf = 0; + }); + }; + + const updateZoomLayout = () => { + const w = baseCanvas.width || 1, + h = baseCanvas.height || 1; + baseCanvas.style.width = w + 'px'; + baseCanvas.style.height = h + 'px'; + maskCanvas.style.width = w + 'px'; + maskCanvas.style.height = h + 'px'; + canvasStack.style.width = w + 'px'; + canvasStack.style.height = h + 'px'; + applyPan(); + }; + const applyZoom = (z) => { + _zoomLevel = Math.max(0.05, Math.min(20, z || 1)); + zoomSlider.value = _zoomLevel; + updateZoomLayout(); + if (zoomValue) zoomValue.textContent = `${Math.round(_zoomLevel * 100)}%`; + }; + zoomSlider.addEventListener('input', () => { + applyZoom(parseFloat(zoomSlider.value)); + }); + if (zoomInBtn) + zoomInBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) + 0.1)); + if (zoomOutBtn) + zoomOutBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) - 0.1)); + const computeFitZoom = () => { + const wrapRect = panStage?.getBoundingClientRect(); + if (!wrapRect) return 1; + const w = baseCanvas.width || 1; + const h = baseCanvas.height || 1; + const margin = 10; + const scaleX = (wrapRect.width - margin) / w; + const scaleY = (wrapRect.height - margin) / h; + return Math.max(0.05, Math.min(20, Math.min(scaleX, scaleY))); + }; + if (zoomFitBtn) + zoomFitBtn.addEventListener('click', () => { + applyZoom(computeFitZoom()); + centerInView(); + }); + if (zoomActualBtn) + zoomActualBtn.addEventListener('click', () => { + applyZoom(1); + centerInView(); + }); + + const centerInView = () => { + if (!panStage) return; + const rect = panStage.getBoundingClientRect(); + const w = (baseCanvas.width || 1) * _zoomLevel; + const h = (baseCanvas.height || 1) * _zoomLevel; + panX = Math.floor((rect.width - w) / 2); + panY = Math.floor((rect.height - h) / 2); + applyPan(); + }; + + let isPanning = false; + let startX = 0, + startY = 0, + startPanX = 0, + startPanY = 0; + let allowPan = false; // Space key + let panMode = false; // Explicit pan mode toggle for touch/one-button mice + const isPanMouseButton = (e) => e.button === 1 || e.button === 2; + const setCursor = (val) => { + if (panStage) panStage.style.cursor = val; + }; + const isPanActive = (e) => panMode || allowPan || isPanMouseButton(e); + const updatePanModeBtn = () => { + if (!panModeBtn) return; + panModeBtn.classList.toggle('active', panMode); + panModeBtn.setAttribute('aria-pressed', panMode ? 'true' : 'false'); + }; + if (panModeBtn) { + updatePanModeBtn(); + panModeBtn.addEventListener('click', () => { + panMode = !panMode; + updatePanModeBtn(); + setCursor(panMode ? 'grab' : ''); + }); + } + if (panStage) { + panStage.addEventListener('contextmenu', (e) => { + if (allowPan) e.preventDefault(); + }); + window.addEventListener('keydown', (e) => { + if (e.code === 'Space') { + allowPan = true; + setCursor('grab'); + } + }); + window.addEventListener('keyup', (e) => { + if (e.code === 'Space') { + allowPan = false; + if (!isPanning) setCursor(''); + } + }); + panStage.addEventListener('mousedown', (e) => { + if (!isPanActive(e)) return; + e.preventDefault(); + isPanning = true; + startX = e.clientX; + startY = e.clientY; + startPanX = panX; + startPanY = panY; + setCursor('grabbing'); + }); + window.addEventListener('mousemove', (e) => { + if (!isPanning) return; + const dx = e.clientX - startX; + const dy = e.clientY - startY; + panX = startPanX + dx; + panY = startPanY + dy; + applyPan(); + }); + window.addEventListener('mouseup', () => { + if (isPanning) { + isPanning = false; + setCursor(allowPan ? 'grab' : ''); + } + }); + panStage.addEventListener( + 'wheel', + (e) => { + if (!e.ctrlKey && !e.metaKey) return; + e.preventDefault(); + const rect = panStage.getBoundingClientRect(); + const cx = e.clientX - rect.left - panX; + const cy = e.clientY - rect.top - panY; + const before = _zoomLevel; + const step = Math.max(0.05, Math.min(0.5, Math.abs(e.deltaY) > 20 ? 0.2 : 0.1)); + const next = Math.max(0.05, Math.min(20, before + (e.deltaY > 0 ? -step : step))); + if (next === before) return; + const scale = next / before; + panX = panX - cx * (scale - 1); + panY = panY - cy * (scale - 1); + applyZoom(next); + }, + { passive: false } + ); + let lastTouchDist = null; + let touchStartTime = 0; + let doubleTapTimer = null; + panStage.addEventListener( + 'touchstart', + (e) => { + if (e.touches.length === 1) { + const t = e.touches[0]; + isPanning = true; + startX = t.clientX; + startY = t.clientY; + startPanX = panX; + startPanY = panY; + setCursor('grabbing'); + const now = Date.now(); + if (now - touchStartTime < 300) { + // double tap -> toggle 100%/fit + const z = Math.abs(_zoomLevel - 1) < 0.01 ? computeFitZoom() : 1; + applyZoom(z); + centerInView(); + if (doubleTapTimer) clearTimeout(doubleTapTimer); + } else { + touchStartTime = now; + doubleTapTimer = setTimeout(() => { + doubleTapTimer = null; + }, 320); + } + } else if (e.touches.length === 2) { + // Pinch start + const [a, b] = e.touches; + lastTouchDist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); + } + }, + { passive: true } + ); + panStage.addEventListener( + 'touchmove', + (e) => { + if (e.touches.length === 1 && isPanning) { + const t = e.touches[0]; + const dx = t.clientX - startX; + const dy = t.clientY - startY; + panX = startPanX + dx; + panY = startPanY + dy; + applyPan(); + } else if (e.touches.length === 2 && lastTouchDist != null) { + e.preventDefault(); + const [a, b] = e.touches; + const dist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); + const rect = panStage.getBoundingClientRect(); + const centerX = (a.clientX + b.clientX) / 2 - rect.left - panX; + const centerY = (a.clientY + b.clientY) / 2 - rect.top - panY; + const before = _zoomLevel; + const scale = dist / (lastTouchDist || dist); + const next = Math.max(0.05, Math.min(20, before * scale)); + if (next !== before) { + panX = panX - centerX * (next / before - 1); + panY = panY - centerY * (next / before - 1); + applyZoom(next); + } + lastTouchDist = dist; + } + }, + { passive: false } + ); + panStage.addEventListener('touchend', () => { + isPanning = false; + lastTouchDist = null; + setCursor(panMode || allowPan ? 'grab' : ''); + }); + } + const schedulePreview = () => { + if (_previewTimer) clearTimeout(_previewTimer); + const run = () => { + _previewTimer = null; + _updateResizePreview(); + }; + if (window.requestIdleCallback) { + _previewTimer = setTimeout(() => requestIdleCallback(run, { timeout: 150 }), 50); + } else { + _previewTimer = setTimeout(() => requestAnimationFrame(run), 50); + } + }; + // Track dragging to reduce work and skip dithering during drag + const markDragStart = () => { + _isDraggingSize = true; + }; + const markDragEnd = () => { + _isDraggingSize = false; + schedulePreview(); + }; + widthSlider.addEventListener('pointerdown', markDragStart); + heightSlider.addEventListener('pointerdown', markDragStart); + widthSlider.addEventListener('pointerup', markDragEnd); + heightSlider.addEventListener('pointerup', markDragEnd); + widthSlider.addEventListener('input', () => { + onWidthInput(); + schedulePreview(); + }); + heightSlider.addEventListener('input', () => { + onHeightInput(); + schedulePreview(); + }); + + // Mask painting UX: brush size, modes, row/column fills, and precise coords + let draggingMask = false; + let lastPaintX = -1, + lastPaintY = -1; + let brushSize = 1; + let rowColSize = 1; + let maskMode = 'ignore'; // 'ignore' | 'unignore' | 'toggle' + const brushEl = resizeContainer.querySelector('#maskBrushSize'); + const brushValEl = resizeContainer.querySelector('#maskBrushSizeValue'); + const btnIgnore = resizeContainer.querySelector('#maskModeIgnore'); + const btnUnignore = resizeContainer.querySelector('#maskModeUnignore'); + const btnToggle = resizeContainer.querySelector('#maskModeToggle'); + const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn'); + const invertMaskBtn = resizeContainer.querySelector('#invertMaskBtn'); + const rowColSizeEl = resizeContainer.querySelector('#rowColSize'); + const rowColSizeValEl = resizeContainer.querySelector('#rowColSizeValue'); + + const updateModeButtons = () => { + const map = [ + [btnIgnore, 'ignore'], + [btnUnignore, 'unignore'], + [btnToggle, 'toggle'], + ]; + for (const [el, m] of map) { + if (!el) continue; + const active = maskMode === m; + el.classList.toggle('active', active); + el.setAttribute('aria-pressed', active ? 'true' : 'false'); + } + }; + const setMode = (mode) => { + maskMode = mode; + updateModeButtons(); + }; + if (brushEl && brushValEl) { + brushEl.addEventListener('input', () => { + brushSize = parseInt(brushEl.value, 10) || 1; + brushValEl.textContent = brushSize; + }); + brushValEl.textContent = brushEl.value; + brushSize = parseInt(brushEl.value, 10) || 1; + } + if (rowColSizeEl && rowColSizeValEl) { + rowColSizeEl.addEventListener('input', () => { + rowColSize = parseInt(rowColSizeEl.value, 10) || 1; + rowColSizeValEl.textContent = rowColSize; + }); + rowColSizeValEl.textContent = rowColSizeEl.value; + rowColSize = parseInt(rowColSizeEl.value, 10) || 1; + } + if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore')); + if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore')); + if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle')); + // Initialize button state (default to toggle mode) + updateModeButtons(); + + const mapClientToPixel = (clientX, clientY) => { + // Compute without rounding until final step to avoid drift at higher zoom + const rect = baseCanvas.getBoundingClientRect(); + const scaleX = rect.width / baseCanvas.width; + const scaleY = rect.height / baseCanvas.height; + const dx = (clientX - rect.left) / scaleX; + const dy = (clientY - rect.top) / scaleY; + const x = Math.floor(dx); + const y = Math.floor(dy); + return { x, y }; + }; + + const ensureMask = (w, h) => { + if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { + state.resizeIgnoreMask = new Uint8Array(w * h); + } + }; + + const paintCircle = (cx, cy, radius, value) => { + const w = baseCanvas.width, + h = baseCanvas.height; + ensureMask(w, h); + const r2 = radius * radius; + for (let yy = cy - radius; yy <= cy + radius; yy++) { + if (yy < 0 || yy >= h) continue; + for (let xx = cx - radius; xx <= cx + radius; xx++) { + if (xx < 0 || xx >= w) continue; + const dx = xx - cx, + dy = yy - cy; + if (dx * dx + dy * dy <= r2) { + const idx = yy * w + xx; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { + _maskData[p] = 255; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 150; + } else { + _maskData[p] = 0; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 0; + } + _markDirty(xx, yy); + } + } + } + } + }; + + const paintRow = (y, value) => { + const w = baseCanvas.width, + h = baseCanvas.height; + ensureMask(w, h); + if (y < 0 || y >= h) return; + + // Paint multiple rows based on rowColSize + const halfSize = Math.floor(rowColSize / 2); + const startY = Math.max(0, y - halfSize); + const endY = Math.min(h - 1, y + halfSize); + + for (let rowY = startY; rowY <= endY; rowY++) { + for (let x = 0; x < w; x++) { + const idx = rowY * w + x; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { + _maskData[p] = 255; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 150; + } else { + _maskData[p] = 0; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 0; + } + } + } + if (_maskData) { + _markDirty(0, rowY); + _markDirty(w - 1, rowY); + } + } + }; + + const paintColumn = (x, value) => { + const w = baseCanvas.width, + h = baseCanvas.height; + ensureMask(w, h); + if (x < 0 || x >= w) return; + + // Paint multiple columns based on rowColSize + const halfSize = Math.floor(rowColSize / 2); + const startX = Math.max(0, x - halfSize); + const endX = Math.min(w - 1, x + halfSize); + + for (let colX = startX; colX <= endX; colX++) { + for (let y = 0; y < h; y++) { + const idx = y * w + colX; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { + _maskData[p] = 255; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 150; + } else { + _maskData[p] = 0; + _maskData[p + 1] = 0; + _maskData[p + 2] = 0; + _maskData[p + 3] = 0; + } + } + } + if (_maskData) { + _markDirty(colX, 0); + _markDirty(colX, h - 1); + } + } + }; + + const redrawMaskOverlay = () => { + // Only flush the dirty region; full rebuild happens on size change + _flushDirty(); + }; + + const handlePaint = (e) => { + // Suppress painting while panning + if ((e.buttons & 4) === 4 || (e.buttons & 2) === 2 || allowPan) return; + const { x, y } = mapClientToPixel(e.clientX, e.clientY); + const w = baseCanvas.width, + h = baseCanvas.height; + if (x < 0 || y < 0 || x >= w || y >= h) return; + const radius = Math.max(1, Math.floor(brushSize / 2)); + if (e.shiftKey) { + paintRow(y); + } else if (e.altKey) { + paintColumn(x); + } else { + paintCircle(x, y, radius); + } + lastPaintX = x; + lastPaintY = y; + redrawMaskOverlay(); + }; + + maskCanvas.addEventListener('mousedown', (e) => { + if (e.button === 1 || e.button === 2 || allowPan) return; // let pan handler manage + draggingMask = true; + handlePaint(e); + }); + // Avoid hijacking touch gestures for panning/zooming + maskCanvas.addEventListener( + 'touchstart', + (e) => { + /* let panStage handle */ + }, + { passive: true } + ); + maskCanvas.addEventListener( + 'touchmove', + (e) => { + /* let panStage handle */ + }, + { passive: true } + ); + maskCanvas.addEventListener( + 'touchend', + (e) => { + /* let panStage handle */ + }, + { passive: true } + ); + window.addEventListener('mousemove', (e) => { + if (draggingMask) handlePaint(e); + }); + window.addEventListener('mouseup', () => { + if (draggingMask) { + draggingMask = false; + saveBotSettings(); + } + }); + + if (clearIgnoredBtnEl) + clearIgnoredBtnEl.addEventListener('click', () => { + const w = baseCanvas.width, + h = baseCanvas.height; + if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0); + _ensureMaskOverlayBuffers(w, h, true); + _updateResizePreview(); + saveBotSettings(); + }); + + if (invertMaskBtn) + invertMaskBtn.addEventListener('click', () => { + if (!state.resizeIgnoreMask) return; + for (let i = 0; i < state.resizeIgnoreMask.length; i++) + state.resizeIgnoreMask[i] = state.resizeIgnoreMask[i] ? 0 : 1; + const w = baseCanvas.width, + h = baseCanvas.height; + _ensureMaskOverlayBuffers(w, h, true); + _updateResizePreview(); + saveBotSettings(); + }); + + confirmResize.onclick = async () => { + const newWidth = parseInt(widthSlider.value, 10); + const newHeight = parseInt(heightSlider.value, 10); + + // Generate the final paletted image data + const tempCanvas = document.createElement('canvas'); + const tempCtx = tempCanvas.getContext('2d'); + tempCanvas.width = newWidth; + tempCanvas.height = newHeight; + tempCtx.imageSmoothingEnabled = false; + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight); + const data = imgData.data; + const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + let totalValidPixels = 0; + const mask = + state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight + ? state.resizeIgnoreMask + : null; + + const applyFSDitherFinal = async () => { + const w = newWidth, + h = newHeight; + const n = w * h; + const { work, eligible } = ensureDitherBuffers(n); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + const i4 = idx * 4; + const r = data[i4], + g = data[i4 + 1], + b = data[i4 + 2], + a = data[i4 + 3]; + const masked = mask && mask[idx]; + const isEligible = + !masked && + (state.paintTransparentPixels || a >= tThresh2) && + (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + eligible[idx] = isEligible ? 1 : 0; + work[idx * 3] = r; + work[idx * 3 + 1] = g; + work[idx * 3 + 2] = b; + if (!isEligible) { + data[i4 + 3] = 0; + } + } + // Yield to keep UI responsive + if ((y & 15) === 0) await Promise.resolve(); + } + + const diffuse = (nx, ny, er, eg, eb, factor) => { + if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; + const nidx = ny * w + nx; + if (!eligible[nidx]) return; + const base = nidx * 3; + work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); + work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); + work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); + }; + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + if (!eligible[idx]) continue; + const base = idx * 3; + const r0 = work[base], + g0 = work[base + 1], + b0 = work[base + 2]; + const [nr, ng, nb] = Utils.findClosestPaletteColor( + r0, + g0, + b0, + state.activeColorPalette + ); + const i4 = idx * 4; + data[i4] = nr; + data[i4 + 1] = ng; + data[i4 + 2] = nb; + data[i4 + 3] = 255; + totalValidPixels++; + + const er = r0 - nr; + const eg = g0 - ng; + const eb = b0 - nb; + + diffuse(x + 1, y, er, eg, eb, 7 / 16); + diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); + diffuse(x, y + 1, er, eg, eb, 5 / 16); + diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); + } + // Yield every row to reduce jank + await Promise.resolve(); + } + }; + + if (state.ditheringEnabled) { + await applyFSDitherFinal(); + } else { + for (let i = 0; i < data.length; i += 4) { + const r = data[i], + g = data[i + 1], + b = data[i + 2], + a = data[i + 3]; + const masked = mask && mask[i >> 2]; + const isTransparent = (!state.paintTransparentPixels && a < tThresh2) || masked; + const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(r, g, b); + if (isTransparent || isWhiteAndSkipped) { + data[i + 3] = 0; // overlay transparency + continue; + } + totalValidPixels++; + const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + data[i] = nr; + data[i + 1] = ng; + data[i + 2] = nb; + data[i + 3] = 255; + } + } + tempCtx.putImageData(imgData, 0, 0); + + // Save the final pixel data for painting + // Persist the paletted (and possibly dithered) pixels so painting uses the same output seen in overlay + const palettedPixels = new Uint8ClampedArray(imgData.data); + state.imageData.pixels = palettedPixels; + state.imageData.width = newWidth; + state.imageData.height = newHeight; + state.imageData.totalPixels = totalValidPixels; + state.totalPixels = totalValidPixels; + state.paintedPixels = 0; + + state.resizeSettings = { + baseWidth: width, + baseHeight: height, + width: newWidth, + height: newHeight, + }; + saveBotSettings(); + + const finalImageBitmap = await createImageBitmap(tempCanvas); + await overlayManager.setImage(finalImageBitmap); + overlayManager.enable(); + toggleOverlayBtn.classList.add('active'); + toggleOverlayBtn.setAttribute('aria-pressed', 'true'); + + // Keep state.imageData.processor as the original-based source; painting uses paletted pixels already stored + + updateStats(); + updateUI('resizeSuccess', 'success', { + width: newWidth, + height: newHeight, + }); + closeResizeDialog(); + }; + + downloadPreviewBtn.onclick = () => { + try { + const w = baseCanvas.width, + h = baseCanvas.height; + const out = document.createElement('canvas'); + out.width = w; + out.height = h; + const octx = out.getContext('2d'); + octx.imageSmoothingEnabled = false; + octx.drawImage(baseCanvas, 0, 0); + octx.drawImage(maskCanvas, 0, 0); + const link = document.createElement('a'); + link.download = 'wplace-preview.png'; + link.href = out.toDataURL(); + link.click(); + } catch (e) { + console.warn('Failed to download preview:', e); + } + }; + + cancelResize.onclick = closeResizeDialog; + + resizeOverlay.style.display = 'block'; + resizeContainer.style.display = 'block'; + + // Reinitialize color palette with current available colors + initializeColorPalette(resizeContainer); + + _updateResizePreview(); + _resizeDialogCleanup = () => { + try { + zoomSlider.replaceWith(zoomSlider.cloneNode(true)); + } catch { } + try { + if (zoomInBtn) zoomInBtn.replaceWith(zoomInBtn.cloneNode(true)); + } catch { } + try { + if (zoomOutBtn) zoomOutBtn.replaceWith(zoomOutBtn.cloneNode(true)); + } catch { } + }; + setTimeout(() => { + if (typeof computeFitZoom === 'function') { + const z = computeFitZoom(); + if (!isNaN(z) && isFinite(z)) { + applyZoom(z); + centerInView(); + } + } else { + centerInView(); + } + }, 0); + } + + function closeResizeDialog() { + try { + if (typeof _resizeDialogCleanup === 'function') { + _resizeDialogCleanup(); + } + } catch { } + resizeOverlay.style.display = 'none'; + resizeContainer.style.display = 'none'; + _updateResizePreview = () => { }; + try { + if (typeof cancelAnimationFrame === 'function' && _panRaf) { + cancelAnimationFrame(_panRaf); + } + } catch { } + try { + if (_previewTimer) { + clearTimeout(_previewTimer); + _previewTimer = null; + } + } catch { } + _maskImageData = null; + _maskData = null; + _dirty = null; + _ditherWorkBuf = null; + _ditherEligibleBuf = null; + _resizeDialogCleanup = null; + } + + if (uploadBtn) { + uploadBtn.addEventListener('click', async () => { + const availableColors = Utils.extractAvailableColors(); + if (availableColors === null || availableColors.length < 10) { + updateUI('noColorsFound', 'error'); + Utils.showAlert(Utils.t('noColorsFound'), 'error'); + return; + } + + if (!state.colorsChecked) { + state.availableColors = availableColors; + state.colorsChecked = true; + updateUI('colorsFound', 'success', { count: availableColors.length }); + updateStats(); + selectPosBtn.disabled = false; + // Only enable resize button if image is also loaded + if (state.imageLoaded) { + resizeBtn.disabled = false; + } + } + + try { + updateUI('loadingImage', 'default'); + const imageSrc = await Utils.createImageUploader(); + if (!imageSrc) { + updateUI('colorsFound', 'success', { + count: state.availableColors.length, + }); + return; + } + + const processor = new ImageProcessor(imageSrc); + await processor.load(); + + const { width, height } = processor.getDimensions(); + const pixels = processor.getPixelData(); + + let totalValidPixels = 0; + for (let i = 0; i < pixels.length; i += 4) { + const isTransparent = + !state.paintTransparentPixels && + pixels[i + 3] < (state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD); + const isWhiteAndSkipped = + !state.paintWhitePixels && + Utils.isWhitePixel(pixels[i], pixels[i + 1], pixels[i + 2]); + if (!isTransparent && !isWhiteAndSkipped) { + totalValidPixels++; + } + } + + state.imageData = { + width, + height, + pixels, + totalPixels: totalValidPixels, + processor, + }; + + state.totalPixels = totalValidPixels; + state.paintedPixels = 0; + state.imageLoaded = true; + state.lastPosition = { x: 0, y: 0 }; + + // Initialize painted map for tracking + Utils.initializePaintedMap(width, height); + + // New image: clear previous resize settings + state.resizeSettings = null; + // Also clear any previous ignore mask + state.resizeIgnoreMask = null; + // Save original image for this browser (dataUrl + dims) + state.originalImage = { dataUrl: imageSrc, width, height }; + saveBotSettings(); + + // Use the original image for the overlay initially + const imageBitmap = await createImageBitmap(processor.img); + await overlayManager.setImage(imageBitmap); + overlayManager.enable(); + toggleOverlayBtn.disabled = false; + toggleOverlayBtn.classList.add('active'); + toggleOverlayBtn.setAttribute('aria-pressed', 'true'); + + // Only enable resize button if colors have also been captured + if (state.colorsChecked) { + resizeBtn.disabled = false; + } + saveBtn.disabled = false; + + if (state.startPosition) { + startBtn.disabled = false; + } + + updateStats(); + updateDataButtons(); + updateUI('imageLoaded', 'success', { count: totalValidPixels }); + } catch { + updateUI('imageError', 'error'); + } + }); + } + + if (resizeBtn) { + resizeBtn.addEventListener('click', () => { + if (state.imageLoaded && state.imageData.processor && state.colorsChecked) { + showResizeDialog(state.imageData.processor); + } else if (!state.colorsChecked) { + Utils.showAlert(Utils.t('uploadImageFirstColors'), 'warning'); + } + }); + } + + if (selectPosBtn) { + selectPosBtn.addEventListener('click', async () => { + if (state.selectingPosition) return; + + state.selectingPosition = true; + state.startPosition = null; + state.region = null; + startBtn.disabled = true; + + Utils.showAlert(Utils.t('selectPositionAlert'), 'info'); + updateUI('waitingPosition', 'default'); + + const tempFetch = async (url, options) => { + if ( + typeof url === 'string' && + url.includes('https://backend.wplace.live/s0/pixel/') && + options?.method?.toUpperCase() === 'POST' + ) { + try { + const response = await originalFetch(url, options); + const clonedResponse = response.clone(); + const data = await clonedResponse.json(); + + if (data?.painted === 1) { + const regionMatch = url.match(/\/pixel\/(\d+)\/(\d+)/); + if (regionMatch && regionMatch.length >= 3) { + state.region = { + x: Number.parseInt(regionMatch[1]), + y: Number.parseInt(regionMatch[2]), + }; + } + + const payload = JSON.parse(options.body); + if (payload?.coords && Array.isArray(payload.coords)) { + state.startPosition = { + x: payload.coords[0], + y: payload.coords[1], + }; + state.lastPosition = { x: 0, y: 0 }; + + await overlayManager.setPosition(state.startPosition, state.region); + + if (state.imageLoaded) { + startBtn.disabled = false; + } + + window.fetch = originalFetch; + state.selectingPosition = false; + updateUI('positionSet', 'success'); + } + } + + return response; + } catch { + return originalFetch(url, options); + } + } + return originalFetch(url, options); + }; + + const originalFetch = window.fetch; + window.fetch = tempFetch; + + setTimeout(() => { + if (state.selectingPosition) { + window.fetch = originalFetch; + state.selectingPosition = false; + updateUI('positionTimeout', 'error'); + Utils.showAlert(Utils.t('positionTimeout'), 'error'); + } + }, 120000); + }); + } + + async function startPainting() { + if (!state.imageLoaded || !state.startPosition || !state.region) { + updateUI('missingRequirements', 'error'); + return; + } + await ensureToken(); + if (!turnstileToken) return; + + state.running = true; + state.stopFlag = false; + startBtn.disabled = true; + stopBtn.disabled = false; + uploadBtn.disabled = true; + selectPosBtn.disabled = true; + resizeBtn.disabled = true; + saveBtn.disabled = true; + toggleOverlayBtn.disabled = true; + + updateUI('startPaintingMsg', 'success'); + + try { + await processImage(); + } catch (e) { + console.error('Unexpected error:', e); + updateUI('paintingError', 'error'); + } finally { + state.running = false; + stopBtn.disabled = true; + saveBtn.disabled = false; + + if (state.stopFlag) { + startBtn.disabled = false; + } else { + startBtn.disabled = true; + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + resizeBtn.disabled = false; + } + toggleOverlayBtn.disabled = false; + } + } + + if (startBtn) { + startBtn.addEventListener('click', startPainting); + } + + if (stopBtn) { + stopBtn.addEventListener('click', () => { + state.stopFlag = true; + state.running = false; + stopBtn.disabled = true; + updateUI('paintingStoppedByUser', 'warning'); + + if (state.imageLoaded && state.paintedPixels > 0) { + Utils.saveProgress(); + Utils.showAlert(Utils.t('autoSaved'), 'success'); + } + }); + } + + const checkSavedProgress = () => { + const savedData = Utils.loadProgress(); + if (savedData && savedData.state.paintedPixels > 0) { + const savedDate = new Date(savedData.timestamp).toLocaleString(); + const progress = Math.round( + (savedData.state.paintedPixels / savedData.state.totalPixels) * 100 + ); + + Utils.showAlert( + `${Utils.t('savedDataFound')}\n\n` + + `Saved: ${savedDate}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + + `${Utils.t('clickLoadToContinue')}`, + 'info' + ); + } + }; + + setTimeout(checkSavedProgress, 1000); + + if (cooldownSlider && cooldownInput && cooldownValue && cooldownDecrease && cooldownIncrease) { + const updateCooldown = (newValue) => { + const threshold = Math.max(1, Math.min(state.maxCharges || 999, parseInt(newValue))); + state.cooldownChargeThreshold = threshold; + + // Update both controls (value shows in input, label shows unit only) + cooldownSlider.value = threshold; + cooldownInput.value = threshold; + cooldownValue.textContent = `${Utils.t('charges')}`; + + saveBotSettings(); + NotificationManager.resetEdgeTracking(); // prevent spurious notify after threshold change + }; + + // Slider event listener + cooldownSlider.addEventListener('input', (e) => { + updateCooldown(e.target.value); + }); + + // Number input event listener + cooldownInput.addEventListener('input', (e) => { + updateCooldown(e.target.value); + }); + + // Decrease button + cooldownDecrease.addEventListener('click', () => { + updateCooldown(parseInt(cooldownInput.value) - 1); + }); + + // Increase button + cooldownIncrease.addEventListener('click', () => { + updateCooldown(parseInt(cooldownInput.value) + 1); + }); + + // Add scroll-to-adjust for cooldown slider + Utils.createScrollToAdjust(cooldownSlider, updateCooldown, 1, state.maxCharges, 1); + } + + loadBotSettings(); + // Ensure notification poller reflects current settings + NotificationManager.syncFromState(); + } + + function getMsToTargetCharges(current, target, cooldown, intervalMs = 0) { + const remainingCharges = target - current; + return Math.max(0, remainingCharges * cooldown - intervalMs); + } + + function updateChargesThresholdUI(intervalMs) { + if (state.stopFlag) return; + + const threshold = state.cooldownChargeThreshold; + const remainingMs = getMsToTargetCharges( + state.preciseCurrentCharges, + threshold, + state.cooldown, + intervalMs + ); + const timeText = Utils.msToTimeText(remainingMs); + + updateUI( + 'noChargesThreshold', + 'warning', + { + threshold, + current: state.displayCharges, + time: timeText, + }, + true + ); + } + + function generateCoordinates(width, height, mode, direction, snake, blockWidth, blockHeight) { + const coords = []; + console.log( + 'Generating coordinates with \n mode:', + mode, + '\n direction:', + direction, + '\n snake:', + snake, + '\n blockWidth:', + blockWidth, + '\n blockHeight:', + blockHeight + ); + // --------- Standard 4 corners traversal ---------- + let xStart, xEnd, xStep; + let yStart, yEnd, yStep; + switch (direction) { + case 'top-left': + xStart = 0; + xEnd = width; + xStep = 1; + yStart = 0; + yEnd = height; + yStep = 1; + break; + case 'top-right': + xStart = width - 1; + xEnd = -1; + xStep = -1; + yStart = 0; + yEnd = height; + yStep = 1; + break; + case 'bottom-left': + xStart = 0; + xEnd = width; + xStep = 1; + yStart = height - 1; + yEnd = -1; + yStep = -1; + break; + case 'bottom-right': + xStart = width - 1; + xEnd = -1; + xStep = -1; + yStart = height - 1; + yEnd = -1; + yStep = -1; + break; + default: + throw new Error(`Unknown direction: ${direction}`); + } + + // --------- Traversal modes ---------- + if (mode === 'rows') { + for (let y = yStart; y !== yEnd; y += yStep) { + if (snake && (y - yStart) % 2 !== 0) { + for (let x = xEnd - xStep; x !== xStart - xStep; x -= xStep) { + coords.push([x, y]); + } + } else { + for (let x = xStart; x !== xEnd; x += xStep) { + coords.push([x, y]); + } + } + } + } else if (mode === 'columns') { + for (let x = xStart; x !== xEnd; x += xStep) { + if (snake && (x - xStart) % 2 !== 0) { + for (let y = yEnd - yStep; y !== yStart - yStep; y -= yStep) { + coords.push([x, y]); + } + } else { + for (let y = yStart; y !== yEnd; y += yStep) { + coords.push([x, y]); + } + } + } + } else if (mode === 'circle-out') { + const cx = Math.floor(width / 2); + const cy = Math.floor(height / 2); + const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy)); + + for (let r = 0; r <= maxRadius; r++) { + for (let y = cy - r; y <= cy + r; y++) { + for (let x = cx - r; x <= cx + r; x++) { + if (x >= 0 && x < width && y >= 0 && y < height) { + const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy)); + if (dist === r) coords.push([x, y]); + } + } + } + } + } else if (mode === 'circle-in') { + const cx = Math.floor(width / 2); + const cy = Math.floor(height / 2); + const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy)); + + for (let r = maxRadius; r >= 0; r--) { + for (let y = cy - r; y <= cy + r; y++) { + for (let x = cx - r; x <= cx + r; x++) { + if (x >= 0 && x < width && y >= 0 && y < height) { + const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy)); + if (dist === r) coords.push([x, y]); + } + } + } + } + } else if (mode === 'blocks' || mode === 'shuffle-blocks') { + const blocks = []; + for (let by = 0; by < height; by += blockHeight) { + for (let bx = 0; bx < width; bx += blockWidth) { + const block = []; + for (let y = by; y < Math.min(by + blockHeight, height); y++) { + for (let x = bx; x < Math.min(bx + blockWidth, width); x++) { + block.push([x, y]); + } + } + blocks.push(block); + } + } + + if (mode === 'shuffle-blocks') { + // Simple Fisher-Yates shuffle + for (let i = blocks.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [blocks[i], blocks[j]] = [blocks[j], blocks[i]]; + } + } + + // Concatenate all blocks + for (const block of blocks) { + coords.push(...block); + } + } else { + throw new Error(`Unknown mode: ${mode}`); + } + + return coords; + } + + async function flushPixelBatch(pixelBatch) { + if (!pixelBatch || pixelBatch.pixels.length === 0) return true; + + const batchSize = pixelBatch.pixels.length; + console.log( + `📦 Sending batch with ${batchSize} pixels (region: ${pixelBatch.regionX},${pixelBatch.regionY})` + ); + const success = await sendBatchWithRetry( + pixelBatch.pixels, + pixelBatch.regionX, + pixelBatch.regionY + ); + if (success) { + pixelBatch.pixels.forEach((p) => { + state.paintedPixels++; + Utils.markPixelPainted(p.x, p.y, pixelBatch.regionX, pixelBatch.regionY); + }); + state.fullChargeData = { + ...state.fullChargeData, + spentSinceShot: state.fullChargeData.spentSinceShot + batchSize, + }; + updateStats(); + updateUI('paintingProgress', 'default', { + painted: state.paintedPixels, + total: state.totalPixels, + }); + Utils.performSmartSave(); + + if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && batchSize > 0) { + const delayPerPixel = 1000 / state.paintingSpeed; + const totalDelay = Math.max(100, delayPerPixel * batchSize); + await Utils.sleep(totalDelay); + } + } else { + console.error(`❌ Batch failed permanently after retries. Stopping painting.`); + state.stopFlag = true; + updateUI('paintingBatchFailed', 'error'); + } + + pixelBatch.pixels = []; + return success; + } + + async function processImage() { + const { width, height, pixels } = state.imageData; + const { x: startX, y: startY } = state.startPosition; + const { x: regionX, y: regionY } = state.region; + + // todo force load tiles + const tilesReady = await overlayManager.waitForTiles( + regionX, + regionY, + width, + height, + startX, + startY, + 10000 // timeout 10s + ); + + if (!tilesReady) { + updateUI('overlayTilesNotLoaded', 'error'); + state.stopFlag = true; + return; + } + + let pixelBatch = null; + let skippedPixels = { + transparent: 0, + white: 0, + alreadyPainted: 0, + colorUnavailable: 0, + }; + + const transparencyThreshold = + state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + + function checkPixelEligibility(x, y) { + const idx = (y * width + x) * 4; + const r = pixels[idx], + g = pixels[idx + 1], + b = pixels[idx + 2], + a = pixels[idx + 3]; + + if (!state.paintTransparentPixels && a < transparencyThreshold) + return { + eligible: false, + reason: 'transparent', + }; + if (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) + return { + eligible: false, + reason: 'white', + }; + + let targetRgb = Utils.isWhitePixel(r, g, b) + ? [255, 255, 255] + : Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + + // Template color ID, normalized/mapped to the nearest available color in our palette. + // Example: template requires "Slate", but we only have "Dark Gray" available + // → mappedTargetColorId = ID of Dark Gray. + // + // If `state.paintUnavailablePixels` is enabled, the painting would stop earlier + // because "Slate" was not found (null returned). + // + // Else, the template "Slate" is mapped to the closest available color (e.g., "Dark Gray"), + // and we proceed with painting using that mapped color. + // + // In this case, if the canvas pixel is already Slate (mapped to available Dark Gray), + // we skip painting, since template and canvas both resolve to the same available color (Dark Gray). + const mappedTargetColorId = Utils.resolveColor( + targetRgb, + state.availableColors, + !state.paintUnavailablePixels + ); + + // Technically, checking only `!mappedTargetColorId.id` would be enough, + // but combined with `state.paintUnavailablePixels` it makes the logic explicit: + // we only skip when the template color cannot be mapped AND strict mode is on. + if (!state.paintUnavailablePixels && !mappedTargetColorId.id) { + return { + eligible: false, + reason: 'colorUnavailable', + r, + g, + b, + a, + mappedColorId: mappedTargetColorId.id, + }; + } + return { eligible: true, r, g, b, a, mappedColorId: mappedTargetColorId.id }; + } + + function skipPixel(reason, id, rgb, x, y) { + if (reason !== 'transparent') { + console.log(`Skipped pixel for ${reason} (id: ${id}, (${rgb.join(', ')})) at (${x}, ${y})`); + } + skippedPixels[reason]++; + } + + try { + const coords = generateCoordinates( + width, + height, + state.coordinateMode, + state.coordinateDirection, + state.coordinateSnake, + state.blockWidth, + state.blockHeight + ); + + outerLoop: for (const [x, y] of coords) { + if (state.stopFlag) { + if (pixelBatch && pixelBatch.pixels.length > 0) { + console.log( + `🎯 Sending last batch before stop with ${pixelBatch.pixels.length} pixels` + ); + await flushPixelBatch(pixelBatch); + } + state.lastPosition = { x, y }; + updateUI('paintingPaused', 'warning', { x, y }); + // noinspection UnnecessaryLabelOnBreakStatementJS + break outerLoop; + } + + const targetPixelInfo = checkPixelEligibility(x, y); + let absX = startX + x; + let absY = startY + y; + + let adderX = Math.floor(absX / 1000); + let adderY = Math.floor(absY / 1000); + let pixelX = absX % 1000; + let pixelY = absY % 1000; + + // Template color ID, normalized/mapped to the nearest available color in our palette. + // Example: template requires "Slate", but we only have "Dark Gray" available + // → mappedTargetColorId = ID of Dark Gray. + // + // If `state.paintUnavailablePixels` is enabled, the painting would stop earlier + // because "Slate" was not found (null returned). + // + // Else, the template "Slate" is mapped to the closest available color (e.g., "Dark Gray"), + // and we proceed with painting using that mapped color. + // + // In this case, if the canvas pixel is already Slate (mapped to available Dark Gray), + // we skip painting, since template and canvas both resolve to the same available color (Dark Gray). + const targetMappedColorId = targetPixelInfo.mappedColorId; + + if (!targetPixelInfo.eligible) { + skipPixel( + targetPixelInfo.reason, + targetMappedColorId, + [targetPixelInfo.r, targetPixelInfo.g, targetPixelInfo.b], + pixelX, + pixelY + ); + continue; + } + + // console.log(`[DEBUG] Pixel at (${pixelX}, ${pixelY}) eligible: RGB=${targetPixelInfo.r}, ${targetPixelInfo.g}, ${targetPixelInfo.b}, + // alpha=${targetPixelInfo.a}, mappedColorId=${targetMappedColorId}`); + + if ( + !pixelBatch || + pixelBatch.regionX !== regionX + adderX || + pixelBatch.regionY !== regionY + adderY + ) { + if (pixelBatch && pixelBatch.pixels.length > 0) { + console.log( + `🌍 Sending region-change batch with ${pixelBatch.pixels.length} pixels (switching to region ${regionX + adderX + },${regionY + adderY})` + ); + const success = await flushPixelBatch(pixelBatch); + + if (success) { + if ( + CONFIG.PAINTING_SPEED_ENABLED && + state.paintingSpeed > 0 && + pixelBatch.pixels.length > 0 + ) { + const batchDelayFactor = Math.max(1, 100 / state.paintingSpeed); + const totalDelay = Math.max(100, batchDelayFactor * pixelBatch.pixels.length); + await Utils.sleep(totalDelay); + } + updateStats(); + } else { + console.error(`❌ Batch failed permanently after retries. Stopping painting.`); + state.stopFlag = true; + updateUI('paintingBatchFailed', 'error'); + // noinspection UnnecessaryLabelOnBreakStatementJS + break outerLoop; + } + } + + pixelBatch = { + regionX: regionX + adderX, + regionY: regionY + adderY, + pixels: [], + }; + } + + try { + const tileKeyParts = [pixelBatch.regionX, pixelBatch.regionY]; + + const tilePixelRGBA = await overlayManager.getTilePixelColor( + tileKeyParts[0], + tileKeyParts[1], + pixelX, + pixelY + ); + + if (tilePixelRGBA && Array.isArray(tilePixelRGBA)) { + // Resolve the actual canvas pixel color to the closest available color. + // (The raw canvas RGB [er, eg, eb] is mapped into state.availableColors) + // so that comparison is consistent with targetMappedColorId. + const mappedCanvasColor = Utils.resolveColor( + tilePixelRGBA.slice(0, 3), + state.availableColors + ); + const isMatch = mappedCanvasColor.id === targetMappedColorId; + if (isMatch) { + skipPixel( + 'alreadyPainted', + targetMappedColorId, + [targetPixelInfo.r, targetPixelInfo.g, targetPixelInfo.b], + pixelX, + pixelY + ); + continue; + } + console.debug( + `[COMPARE] Pixel at 📍 (${pixelX}, ${pixelY}) in region (${regionX + adderX + }, ${regionY + adderY})\n` + + ` ├── Current color: rgb(${tilePixelRGBA.slice(0, 3).join(', ')}) (id: ${mappedCanvasColor.id})\n` + + ` ├── Target color: rgb(${targetPixelInfo.r}, ${targetPixelInfo.g}, ${targetPixelInfo.b}) (id: ${targetMappedColorId})\n` + + ` └── Status: ${isMatch ? '✅ Already painted → SKIP' : '🔴 Needs paint → PAINT' + }\n` + ); + } + } catch (e) { + console.error(`[DEBUG] Error checking existing pixel at (${pixelX}, ${pixelY}):`, e); + updateUI('paintingPixelCheckFailed', 'error', { x: pixelX, y: pixelY }); + state.stopFlag = true; + // noinspection UnnecessaryLabelOnBreakStatementJS + break outerLoop; + } + + pixelBatch.pixels.push({ + x: pixelX, + y: pixelY, + color: targetMappedColorId, + localX: x, + localY: y, + }); + + const maxBatchSize = calculateBatchSize(); + if (pixelBatch.pixels.length >= maxBatchSize) { + const modeText = + state.batchMode === 'random' + ? `random (${state.randomBatchMin}-${state.randomBatchMax})` + : 'normal'; + console.log( + `📦 Sending batch with ${pixelBatch.pixels.length} pixels (mode: ${modeText}, target: ${maxBatchSize})` + ); + const success = await flushPixelBatch(pixelBatch); + if (!success) { + console.error(`❌ Batch failed permanently after retries. Stopping painting.`); + state.stopFlag = true; + updateUI('paintingBatchFailed', 'error'); + // noinspection UnnecessaryLabelOnBreakStatementJS + break outerLoop; + } + + pixelBatch.pixels = []; + } + + if (state.displayCharges < state.cooldownChargeThreshold && !state.stopFlag) { + await Utils.dynamicSleep(() => { + if (state.displayCharges >= state.cooldownChargeThreshold) { + NotificationManager.maybeNotifyChargesReached(true); + return 0; + } + if (state.stopFlag) return 0; + return getMsToTargetCharges( + state.preciseCurrentCharges, + state.cooldownChargeThreshold, + state.cooldown + ); + }); + } + + if (state.stopFlag) { + // noinspection UnnecessaryLabelOnBreakStatementJS + break outerLoop; + } + } + + if (pixelBatch && pixelBatch.pixels.length > 0 && !state.stopFlag) { + console.log(`🏁 Sending final batch with ${pixelBatch.pixels.length} pixels`); + const success = await flushPixelBatch(pixelBatch); + if (!success) { + console.warn( + `⚠️ Final batch failed with ${pixelBatch.pixels.length} pixels after all retries.` + ); + } + } + } finally { + if (window._chargesInterval) clearInterval(window._chargesInterval); + window._chargesInterval = null; + } + + if (state.stopFlag) { + // Save progress when stopped to preserve painted map + Utils.saveProgress(); + } else { + updateUI('paintingComplete', 'success', { count: state.paintedPixels }); + state.lastPosition = { x: 0, y: 0 }; + // Keep painted map until user starts new project + // state.paintedMap = null // Commented out to preserve data + Utils.saveProgress(); // Save final complete state + overlayManager.clear(); + const toggleOverlayBtn = document.getElementById('toggleOverlayBtn'); + if (toggleOverlayBtn) { + toggleOverlayBtn.classList.remove('active'); + toggleOverlayBtn.disabled = true; + } + } + + // Log skip statistics + console.log(`📊 Pixel Statistics:`); + console.log(` Painted: ${state.paintedPixels}`); + console.log(` Skipped - Transparent: ${skippedPixels.transparent}`); + console.log(` Skipped - White (disabled): ${skippedPixels.white}`); + console.log(` Skipped - Already painted: ${skippedPixels.alreadyPainted}`); + console.log(` Skipped - Color Unavailable: ${skippedPixels.colorUnavailable}`); + console.log( + ` Total processed: ${state.paintedPixels + + skippedPixels.transparent + + skippedPixels.white + + skippedPixels.alreadyPainted + + skippedPixels.colorUnavailable + }` + ); + + updateStats(); + } + + // Helper function to calculate batch size based on mode + function calculateBatchSize() { + let targetBatchSize; + + if (state.batchMode === 'random') { + // Generate random batch size within the specified range + const min = Math.max(1, state.randomBatchMin); + const max = Math.max(min, state.randomBatchMax); + targetBatchSize = Math.floor(Math.random() * (max - min + 1)) + min; + console.log(`🎲 Random batch size generated: ${targetBatchSize} (range: ${min}-${max})`); + } else { + // Normal mode - use the fixed paintingSpeed value + targetBatchSize = state.paintingSpeed; + } + + // Always limit by available charges + const maxAllowed = state.displayCharges; + const finalBatchSize = Math.min(targetBatchSize, maxAllowed); + + return finalBatchSize; + } + + // Helper function to retry batch until success with exponential backoff + async function sendBatchWithRetry(pixels, regionX, regionY, maxRetries = MAX_BATCH_RETRIES) { + let attempt = 0; + while (attempt < maxRetries && !state.stopFlag) { + attempt++; + console.log( + `🔄 Attempting to send batch (attempt ${attempt}/${maxRetries}) for region ${regionX},${regionY} with ${pixels.length} pixels` + ); + + const result = await sendPixelBatch(pixels, regionX, regionY); + + if (result === true) { + console.log(`✅ Batch succeeded on attempt ${attempt}`); + return true; + } else if (result === 'token_error') { + console.log(`🔑 Token error on attempt ${attempt}, regenerating...`); + updateUI('captchaSolving', 'warning'); + try { + await handleCaptcha(); + // Don't count token regeneration as a failed attempt + attempt--; + continue; + } catch (e) { + console.error(`❌ Token regeneration failed on attempt ${attempt}:`, e); + updateUI('captchaFailed', 'error'); + // Wait longer before retrying after token failure + await Utils.sleep(5000); + } + } else { + console.warn(`⚠️ Batch failed on attempt ${attempt}, retrying...`); + // Exponential backoff with jitter + const baseDelay = Math.min(1000 * Math.pow(2, attempt - 1), 30000); // Max 30s + const jitter = Math.random() * 1000; // Add up to 1s random delay + await Utils.sleep(baseDelay + jitter); + } + } + + if (attempt >= maxRetries) { + console.error( + `❌ Batch failed after ${maxRetries} attempts (MAX_BATCH_RETRIES=${MAX_BATCH_RETRIES}). This will stop painting to prevent infinite loops.` + ); + updateUI('paintingError', 'error'); + return false; + } + + return false; + } + + async function sendPixelBatch(pixelBatch, regionX, regionY) { + let token = turnstileToken; + + // Generate new token if we don't have one + if (!token) { + try { + console.log('🔑 Generating Turnstile token for pixel batch...'); + token = await handleCaptcha(); + turnstileToken = token; // Store for potential reuse + } catch (error) { + console.error('❌ Failed to generate Turnstile token:', error); + tokenPromise = new Promise((resolve) => { + _resolveToken = resolve; + }); + return 'token_error'; + } + } + + const coords = new Array(pixelBatch.length * 2); + const colors = new Array(pixelBatch.length); + for (let i = 0; i < pixelBatch.length; i++) { + const pixel = pixelBatch[i]; + coords[i * 2] = pixel.x; + coords[i * 2 + 1] = pixel.y; + colors[i] = pixel.color; + } + + try { + const payload = { coords, colors, t: token, fp: fpStr32 }; + var wasmtoken = await createWasmToken(regionX, regionY, payload); + const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { + method: 'POST', + headers: { 'Content-Type': 'text/plain;charset=UTF-8', "x-pawtect-token": wasmtoken }, + credentials: 'include', + body: JSON.stringify(payload), + }); + + if (res.status === 403) { + let data = null; + try { + data = await res.json(); + } catch (_) { } + console.error('❌ 403 Forbidden. Turnstile token might be invalid or expired.'); + + // Try to generate a new token and retry once + try { + console.log('🔄 Regenerating Turnstile token after 403...'); + token = await handleCaptcha(); + turnstileToken = token; + + // Retry the request with new token + const retryPayload = { coords, colors, t: token, fp: fpStr32 }; + var wasmtoken = await createWasmToken(regionX, regionY, retryPayload); + const retryRes = await fetch( + `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, + { + method: 'POST', + headers: { 'Content-Type': 'text/plain;charset=UTF-8', "x-pawtect-token": wasmtoken }, + credentials: 'include', + body: JSON.stringify(retryPayload), + } + ); + + if (retryRes.status === 403) { + turnstileToken = null; + tokenPromise = new Promise((resolve) => { + _resolveToken = resolve; + }); + return 'token_error'; + } + + const retryData = await retryRes.json(); + return retryData?.painted === pixelBatch.length; + } catch (retryError) { + console.error('❌ Token regeneration failed:', retryError); + turnstileToken = null; + tokenPromise = new Promise((resolve) => { + _resolveToken = resolve; + }); + return 'token_error'; + } + } + + const data = await res.json(); + return data?.painted === pixelBatch.length; + } catch (e) { + console.error('Batch paint request failed:', e); + return false; + } + } + + function saveBotSettings() { + try { + const settings = { + paintingSpeed: state.paintingSpeed, + paintingSpeedEnabled: document.getElementById('enableSpeedToggle')?.checked, + batchMode: state.batchMode, // "normal" or "random" + randomBatchMin: state.randomBatchMin, + randomBatchMax: state.randomBatchMax, + cooldownChargeThreshold: state.cooldownChargeThreshold, + tokenSource: state.tokenSource, // "generator", "hybrid", or "manual" + minimized: state.minimized, + overlayOpacity: state.overlayOpacity, + blueMarbleEnabled: document.getElementById('enableBlueMarbleToggle')?.checked, + ditheringEnabled: state.ditheringEnabled, + colorMatchingAlgorithm: state.colorMatchingAlgorithm, + enableChromaPenalty: state.enableChromaPenalty, + chromaPenaltyWeight: state.chromaPenaltyWeight, + customTransparencyThreshold: state.customTransparencyThreshold, + customWhiteThreshold: state.customWhiteThreshold, + paintWhitePixels: state.paintWhitePixels, + paintTransparentPixels: state.paintTransparentPixels, + resizeSettings: state.resizeSettings, + paintUnavailablePixels: state.paintUnavailablePixels, + coordinateMode: state.coordinateMode, + coordinateDirection: state.coordinateDirection, + coordinateSnake: state.coordinateSnake, + blockWidth: state.blockWidth, + blockHeight: state.blockHeight, // Save ignore mask (as base64) with its dimensions + resizeIgnoreMask: + state.resizeIgnoreMask && + state.resizeSettings && + state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length + ? { + w: state.resizeSettings.width, + h: state.resizeSettings.height, + data: btoa(String.fromCharCode(...state.resizeIgnoreMask)), + } + : null, // Notifications + notificationsEnabled: state.notificationsEnabled, + notifyOnChargesReached: state.notifyOnChargesReached, + notifyOnlyWhenUnfocused: state.notifyOnlyWhenUnfocused, + notificationIntervalMinutes: state.notificationIntervalMinutes, + originalImage: state.originalImage, + }; + CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled; + // AUTO_CAPTCHA_ENABLED is always true - no need to save/load + + localStorage.setItem('wplace-bot-settings', JSON.stringify(settings)); + } catch (e) { + console.warn('Could not save bot settings:', e); + } + } + + function loadBotSettings() { + try { + const saved = localStorage.getItem('wplace-bot-settings'); + if (!saved) return; + const settings = JSON.parse(saved); + + state.paintingSpeed = settings.paintingSpeed || CONFIG.PAINTING_SPEED.DEFAULT; + state.batchMode = settings.batchMode || CONFIG.BATCH_MODE; // Default to "normal" + state.randomBatchMin = settings.randomBatchMin || CONFIG.RANDOM_BATCH_RANGE.MIN; + state.randomBatchMax = settings.randomBatchMax || CONFIG.RANDOM_BATCH_RANGE.MAX; + state.cooldownChargeThreshold = + settings.cooldownChargeThreshold || CONFIG.COOLDOWN_CHARGE_THRESHOLD; + state.tokenSource = settings.tokenSource || CONFIG.TOKEN_SOURCE; // Default to "generator" + state.minimized = settings.minimized ?? false; + CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled ?? false; + CONFIG.AUTO_CAPTCHA_ENABLED = settings.autoCaptchaEnabled ?? false; + state.overlayOpacity = settings.overlayOpacity ?? CONFIG.OVERLAY.OPACITY_DEFAULT; + state.blueMarbleEnabled = settings.blueMarbleEnabled ?? CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT; + state.ditheringEnabled = settings.ditheringEnabled ?? false; + state.colorMatchingAlgorithm = settings.colorMatchingAlgorithm || 'lab'; + state.enableChromaPenalty = settings.enableChromaPenalty ?? true; + state.chromaPenaltyWeight = settings.chromaPenaltyWeight ?? 0.15; + state.customTransparencyThreshold = + settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; + state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; + state.paintWhitePixels = settings.paintWhitePixels ?? true; + state.paintTransparentPixels = settings.paintTransparentPixels ?? false; + state.resizeSettings = settings.resizeSettings ?? null; + state.originalImage = settings.originalImage ?? null; + state.paintUnavailablePixels = settings.paintUnavailablePixels ?? CONFIG.PAINT_UNAVAILABLE; + state.coordinateMode = settings.coordinateMode ?? CONFIG.COORDINATE_MODE; + state.coordinateDirection = settings.coordinateDirection ?? CONFIG.COORDINATE_DIRECTION; + state.coordinateSnake = settings.coordinateSnake ?? CONFIG.COORDINATE_SNAKE; + state.blockWidth = settings.blockWidth ?? CONFIG.COORDINATE_BLOCK_WIDTH; + state.blockHeight = settings.blockHeight ?? CONFIG.COORDINATE_BLOCK_HEIGHT; + // Notifications + state.notificationsEnabled = settings.notificationsEnabled ?? CONFIG.NOTIFICATIONS.ENABLED; + state.notifyOnChargesReached = + settings.notifyOnChargesReached ?? CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED; + state.notifyOnlyWhenUnfocused = + settings.notifyOnlyWhenUnfocused ?? CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED; + state.notificationIntervalMinutes = + settings.notificationIntervalMinutes ?? CONFIG.NOTIFICATIONS.REPEAT_MINUTES; + // Restore ignore mask if dims match current resizeSettings + if ( + settings.resizeIgnoreMask && + settings.resizeIgnoreMask.data && + state.resizeSettings && + settings.resizeIgnoreMask.w === state.resizeSettings.width && + settings.resizeIgnoreMask.h === state.resizeSettings.height + ) { + try { + const bin = atob(settings.resizeIgnoreMask.data); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i); + state.resizeIgnoreMask = arr; + } catch { + state.resizeIgnoreMask = null; + } + } else { + state.resizeIgnoreMask = null; + } + // Initialize coordinate generation UI + const coordinateModeSelect = document.getElementById('coordinateModeSelect'); + if (coordinateModeSelect) coordinateModeSelect.value = state.coordinateMode; + + const coordinateDirectionSelect = document.getElementById('coordinateDirectionSelect'); + if (coordinateDirectionSelect) coordinateDirectionSelect.value = state.coordinateDirection; + + const coordinateSnakeToggle = document.getElementById('coordinateSnakeToggle'); + if (coordinateSnakeToggle) coordinateSnakeToggle.checked = state.coordinateSnake; + + const settingsContainer = document.getElementById('wplace-settings-container'); + const directionControls = settingsContainer.querySelector('#directionControls'); + const snakeControls = settingsContainer.querySelector('#snakeControls'); + const blockControls = settingsContainer.querySelector('#blockControls'); + Utils.updateCoordinateUI({ + mode: state.coordinateMode, + directionControls, + snakeControls, + blockControls, + }); + + const paintUnavailablePixelsToggle = document.getElementById('paintUnavailablePixelsToggle'); + if (paintUnavailablePixelsToggle) { + paintUnavailablePixelsToggle.checked = state.paintUnavailablePixels; + } + + const settingsPaintWhiteToggle = settingsContainer.querySelector('#settingsPaintWhiteToggle'); + if (settingsPaintWhiteToggle) { + settingsPaintWhiteToggle.checked = state.paintWhitePixels; + } + + const settingsPaintTransparentToggle = settingsContainer.querySelector( + '#settingsPaintTransparentToggle' + ); + if (settingsPaintTransparentToggle) { + settingsPaintTransparentToggle.checked = state.paintTransparentPixels; + } + + const speedSlider = document.getElementById('speedSlider'); + const speedInput = document.getElementById('speedInput'); + const speedValue = document.getElementById('speedValue'); + if (speedSlider) speedSlider.value = state.paintingSpeed; + if (speedInput) speedInput.value = state.paintingSpeed; + if (speedValue) speedValue.textContent = `pixels`; + + const enableSpeedToggle = document.getElementById('enableSpeedToggle'); + if (enableSpeedToggle) enableSpeedToggle.checked = CONFIG.PAINTING_SPEED_ENABLED; + + // Batch mode UI initialization + const batchModeSelect = document.getElementById('batchModeSelect'); + if (batchModeSelect) batchModeSelect.value = state.batchMode; + + const normalBatchControls = document.getElementById('normalBatchControls'); + const randomBatchControls = document.getElementById('randomBatchControls'); + + // Show/hide appropriate controls based on batch mode + if (normalBatchControls && randomBatchControls) { + if (state.batchMode === 'random') { + normalBatchControls.style.display = 'none'; + randomBatchControls.style.display = 'block'; + } else { + normalBatchControls.style.display = 'block'; + randomBatchControls.style.display = 'none'; + } + } + + const randomBatchMin = document.getElementById('randomBatchMin'); + if (randomBatchMin) randomBatchMin.value = state.randomBatchMin; + + const randomBatchMax = document.getElementById('randomBatchMax'); + if (randomBatchMax) randomBatchMax.value = state.randomBatchMax; + + // AUTO_CAPTCHA_ENABLED is always true - no toggle to set + + const cooldownSlider = document.getElementById('cooldownSlider'); + const cooldownInput = document.getElementById('cooldownInput'); + const cooldownValue = document.getElementById('cooldownValue'); + if (cooldownSlider) cooldownSlider.value = state.cooldownChargeThreshold; + if (cooldownInput) cooldownInput.value = state.cooldownChargeThreshold; + if (cooldownValue) cooldownValue.textContent = `${Utils.t('charges')}`; + + const overlayOpacitySlider = document.getElementById('overlayOpacitySlider'); + if (overlayOpacitySlider) overlayOpacitySlider.value = state.overlayOpacity; + const overlayOpacityValue = document.getElementById('overlayOpacityValue'); + if (overlayOpacityValue) + overlayOpacityValue.textContent = `${Math.round(state.overlayOpacity * 100)}%`; + const enableBlueMarbleToggle = document.getElementById('enableBlueMarbleToggle'); + if (enableBlueMarbleToggle) enableBlueMarbleToggle.checked = state.blueMarbleEnabled; + + const tokenSourceSelect = document.getElementById('tokenSourceSelect'); + if (tokenSourceSelect) tokenSourceSelect.value = state.tokenSource; + + const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); + if (colorAlgorithmSelect) colorAlgorithmSelect.value = state.colorMatchingAlgorithm; + const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); + if (enableChromaPenaltyToggle) enableChromaPenaltyToggle.checked = state.enableChromaPenalty; + const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); + if (chromaPenaltyWeightSlider) chromaPenaltyWeightSlider.value = state.chromaPenaltyWeight; + const chromaWeightValue = document.getElementById('chromaWeightValue'); + if (chromaWeightValue) chromaWeightValue.textContent = state.chromaPenaltyWeight; + const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); + if (transparencyThresholdInput) + transparencyThresholdInput.value = state.customTransparencyThreshold; + const whiteThresholdInput = document.getElementById('whiteThresholdInput'); + if (whiteThresholdInput) whiteThresholdInput.value = state.customWhiteThreshold; + // Notifications UI + const notifEnabledToggle = document.getElementById('notifEnabledToggle'); + if (notifEnabledToggle) notifEnabledToggle.checked = state.notificationsEnabled; + const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); + if (notifOnChargesToggle) notifOnChargesToggle.checked = state.notifyOnChargesReached; + const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); + if (notifOnlyUnfocusedToggle) + notifOnlyUnfocusedToggle.checked = state.notifyOnlyWhenUnfocused; + const notifIntervalInput = document.getElementById('notifIntervalInput'); + if (notifIntervalInput) notifIntervalInput.value = state.notificationIntervalMinutes; + NotificationManager.resetEdgeTracking(); + } catch (e) { + console.warn('Could not load bot settings:', e); + } + } + + // Initialize Turnstile generator integration + console.log('🚀 WPlace Auto-Image with Turnstile Token Generator loaded'); + console.log('🔑 Turnstile token generator: ALWAYS ENABLED (Background mode)'); + console.log('🎯 Manual pixel captcha solving: Available as fallback/alternative'); + console.log('📱 Turnstile widgets: DISABLED - pure background token generation only!'); + + // Function to enable file operations after initial startup setup is complete + function enableFileOperations() { + state.initialSetupComplete = true; + + const loadBtn = document.querySelector('#loadBtn'); + const loadFromFileBtn = document.querySelector('#loadFromFileBtn'); + const uploadBtn = document.querySelector('#uploadBtn'); + + if (loadBtn) { + loadBtn.disabled = false; + loadBtn.title = ''; + // Add a subtle animation to indicate the button is now available + loadBtn.style.animation = 'pulse 0.6s ease-in-out'; + setTimeout(() => { + if (loadBtn) loadBtn.style.animation = ''; + }, 600); + console.log('✅ Load Progress button enabled after initial setup'); + } + + if (loadFromFileBtn) { + loadFromFileBtn.disabled = false; + loadFromFileBtn.title = ''; + // Add a subtle animation to indicate the button is now available + loadFromFileBtn.style.animation = 'pulse 0.6s ease-in-out'; + setTimeout(() => { + if (loadFromFileBtn) loadFromFileBtn.style.animation = ''; + }, 600); + console.log('✅ Load from File button enabled after initial setup'); + } + + if (uploadBtn) { + uploadBtn.disabled = false; + uploadBtn.title = ''; + // Add a subtle animation to indicate the button is now available + uploadBtn.style.animation = 'pulse 0.6s ease-in-out'; + setTimeout(() => { + if (uploadBtn) uploadBtn.style.animation = ''; + }, 600); + console.log('✅ Upload Image button enabled after initial setup'); + } + + // Show a notification that file operations are now available + Utils.showAlert(Utils.t('fileOperationsAvailable'), 'success'); + } + + // Optimized token initialization with better timing and error handling + async function initializeTokenGenerator() { + // Skip if already have valid token + if (isTokenValid()) { + console.log('✅ Valid token already available, skipping initialization'); + updateUI('tokenReady', 'success'); + enableFileOperations(); // Enable file operations since initial setup is complete + return; + } + + try { + console.log('🔧 Initializing Turnstile token generator...'); + updateUI('initializingToken', 'default'); + + console.log('Attempting to load Turnstile script...'); + await Utils.loadTurnstile(); + console.log('Turnstile script loaded. Attempting to generate token...'); + + const token = await handleCaptchaWithRetry(); + if (token) { + setTurnstileToken(token); + console.log('✅ Startup token generated successfully'); + updateUI('tokenReady', 'success'); + Utils.showAlert(Utils.t('tokenGeneratorReady'), 'success'); + enableFileOperations(); // Enable file operations since initial setup is complete + } else { + console.warn( + '⚠️ Startup token generation failed (no token received), will retry when needed' + ); + updateUI('tokenRetryLater', 'warning'); + // Still enable file operations even if initial token generation fails + // Users can load progress and use manual/hybrid modes + enableFileOperations(); + } + } catch (error) { + console.error('❌ Critical error during Turnstile initialization:', error); // More specific error + updateUI('tokenRetryLater', 'warning'); + // Still enable file operations even if initial setup fails + // Users can load progress and use manual/hybrid modes + enableFileOperations(); + // Don't show error alert for initialization failures, just log them + } + } + + // Load theme preference immediately on startup before creating UI + loadThemePreference(); + applyTheme(); + + 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); + } + + // Create test payload + + 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); + } + + // Display results + console.log(''); + console.log('🎉 SUCCESS!'); + console.log('🔑 Full token:'); + console.log(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); + } + + createUI().then(() => { + // Generate token automatically after UI is ready + setTimeout(initializeTokenGenerator, 1000); + + // Attach advanced color matching listeners (resize dialog) + const advancedInit = () => { + const chromaSlider = document.getElementById('chromaPenaltyWeightSlider'); + const chromaValue = document.getElementById('chromaWeightValue'); + const resetBtn = document.getElementById('resetAdvancedColorBtn'); + const algoSelect = document.getElementById('colorAlgorithmSelect'); + const chromaToggle = document.getElementById('enableChromaPenaltyToggle'); + const transInput = document.getElementById('transparencyThresholdInput'); + const whiteInput = document.getElementById('whiteThresholdInput'); + const ditherToggle = document.getElementById('enableDitheringToggle'); + if (algoSelect) + algoSelect.addEventListener('change', (e) => { + state.colorMatchingAlgorithm = e.target.value; + saveBotSettings(); + _updateResizePreview(); + }); + if (chromaToggle) + chromaToggle.addEventListener('change', (e) => { + state.enableChromaPenalty = e.target.checked; + saveBotSettings(); + _updateResizePreview(); + }); + if (chromaSlider && chromaValue) + chromaSlider.addEventListener('input', (e) => { + state.chromaPenaltyWeight = parseFloat(e.target.value) || 0.15; + chromaValue.textContent = state.chromaPenaltyWeight.toFixed(2); + saveBotSettings(); + _updateResizePreview(); + }); + if (transInput) + transInput.addEventListener('change', (e) => { + const v = parseInt(e.target.value, 10); + if (!isNaN(v) && v >= 0 && v <= 255) { + state.customTransparencyThreshold = v; + CONFIG.TRANSPARENCY_THRESHOLD = v; + saveBotSettings(); + _updateResizePreview(); + } + }); + if (whiteInput) + whiteInput.addEventListener('change', (e) => { + const v = parseInt(e.target.value, 10); + if (!isNaN(v) && v >= 200 && v <= 255) { + state.customWhiteThreshold = v; + CONFIG.WHITE_THRESHOLD = v; + saveBotSettings(); + _updateResizePreview(); + } + }); + if (ditherToggle) + ditherToggle.addEventListener('change', (e) => { + state.ditheringEnabled = e.target.checked; + saveBotSettings(); + _updateResizePreview(); + }); + if (resetBtn) + resetBtn.addEventListener('click', () => { + state.colorMatchingAlgorithm = 'lab'; + state.enableChromaPenalty = true; + state.chromaPenaltyWeight = 0.15; + state.customTransparencyThreshold = CONFIG.TRANSPARENCY_THRESHOLD = 100; + state.customWhiteThreshold = CONFIG.WHITE_THRESHOLD = 250; + saveBotSettings(); + const a = document.getElementById('colorAlgorithmSelect'); + if (a) a.value = 'lab'; + const ct = document.getElementById('enableChromaPenaltyToggle'); + if (ct) ct.checked = true; + if (chromaSlider) chromaSlider.value = 0.15; + if (chromaValue) chromaValue.textContent = '0.15'; + if (transInput) transInput.value = 100; + if (whiteInput) whiteInput.value = 250; + _updateResizePreview(); + Utils.showAlert(Utils.t('advancedColorSettingsReset'), 'success'); + }); + }; + // Delay to ensure resize UI built + setTimeout(advancedInit, 500); + + // Add cleanup on page unload + window.addEventListener('beforeunload', () => { + Utils.cleanupTurnstile(); + }); + }); +})(); diff --git a/Extension/scripts/Auto-Repair.js b/Extension/scripts/Auto-Repair.js new file mode 100644 index 0000000..bbcbd92 --- /dev/null +++ b/Extension/scripts/Auto-Repair.js @@ -0,0 +1,2787 @@ +// 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 = ` +
+ Attack +
+

+ PIXEL ART ATTACKED! +

+

+ Detected ${count} damaged pixels! +

+ `; + } else if (type === 'repaired') { + notification.innerHTML = ` +
+ Repaired +
+

+ COMPLETELY REPAIRED! +

+

+ All pixels have been restored! +

+ `; + } + + document.body.appendChild(notification); + + setTimeout(() => { + if (notification.parentNode) { + notification.style.transition = 'opacity 0.5s ease'; + notification.style.opacity = '0'; + setTimeout(() => { + if (notification.parentNode) notification.remove(); + }, 500); + } + }, 3000); + } + + // Peaceful state display in main window + function showPeacefulState() { + const statusDiv = document.getElementById('status'); + if (statusDiv && !state.running) { + statusDiv.innerHTML = ` +
+ Peaceful + No attacks detected - All secure +
+ `; + } + } + + // Typewriter effect for title + function createTypewriterTitle() { + const titleText = "WPlace Autonomous Repair Tool"; + let currentIndex = 0; + const titleElement = document.getElementById('main-title'); + + if (!titleElement) return; + + function typeNext() { + if (currentIndex < titleText.length) { + titleElement.textContent = titleText.substring(0, currentIndex + 1); + currentIndex++; + setTimeout(typeNext, 100); + } else { + setTimeout(() => { + currentIndex = 0; + titleElement.textContent = ''; + setTimeout(typeNext, 1000); + }, 3000); + } + } + + typeNext(); + } + + // Fallback translations + const TEXTS = { + title: 'WPlace Autonomous Repair Tool', + loadFromFile: 'Load Progress File', + repairPixels: 'Repair Pixels', + enableAutoRepair: 'Enable Auto Repair', + repairInterval: 'Check Interval (seconds)', + debug: 'Debug Console', + clearDebug: 'Clear Debug', + scanningForDamage: 'Scanning for damaged pixels...', + damageDetected: 'Damage detected: {count} pixels', + noDamageDetected: 'No damage found', + repairingPixels: 'Repairing {count} damaged pixels...', + repairComplete: 'Repair completed: {repaired} pixels fixed', + autoRepairStarted: 'Auto repair started (every {interval}s)', + autoRepairStopped: 'Auto repair stopped', + fileLoaded: 'Progress file loaded successfully', + invalidFile: 'Invalid file format', + noImageData: 'No image data found in file', + turnstileInstructions: 'Complete the verification', + hideTurnstileBtn: 'Hide', + tokenCapturedSuccess: 'Token captured successfully', + autonomousModeActive: 'Autonomous mode active', + tokenSystemReady: 'Advanced token system ready', + fileOperationsAvailable: 'File operations available', + initializingToken: 'Initializing token system...', + tokenReady: 'Token system ready', + }; + + // UTILIDADES ACTUALIZADAS CON NUEVA IMPLEMENTACIÓN TURNSTILE + const Utils = { + sleep: (ms) => new Promise((r) => setTimeout(r, ms)), + + t: (key, params = {}) => { + let text = TEXTS[key] || key; + Object.keys(params).forEach((param) => { + text = text.replace(`{${param}}`, params[param]); + }); + return text; + }, + + showAlert: (message, type = 'info') => { + const alertDiv = document.createElement('div'); + alertDiv.style.cssText = ` + position: fixed; top: 20px; right: 20px; z-index: 10000; + padding: 12px 16px; border-radius: 8px; color: white; font-weight: 500; + background: ${type === 'success' ? '#28a745' : type === 'error' ? '#dc3545' : type === 'warning' ? '#ffc107' : '#17a2b8'}; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + max-width: 300px; word-wrap: break-word; + `; + alertDiv.textContent = message; + document.body.appendChild(alertDiv); + + setTimeout(() => { + alertDiv.style.transition = 'opacity 0.3s ease'; + alertDiv.style.opacity = '0'; + setTimeout(() => { + if (alertDiv.parentNode) alertDiv.remove(); + }, 300); + }, 4000); + }, + + addDebugLog: (message, type = 'info') => { + const timestamp = new Date().toLocaleTimeString(); + state.debugLogs.push({ timestamp, message, type }); + + if (state.debugLogs.length > 150) { + state.debugLogs = state.debugLogs.slice(-150); + } + + updateDebugConsole(); + console.log(`[${timestamp}] ${message}`); + }, + + waitForSelector: async (selector, interval = 200, timeout = 5000) => { + const start = Date.now(); + while (Date.now() - start < timeout) { + const el = document.querySelector(selector); + if (el) return el; + await Utils.sleep(interval); + } + return null; + }, + + createFileUploader: () => + new Promise((resolve, reject) => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + input.onchange = (e) => { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = () => { + try { + const data = JSON.parse(reader.result); + resolve(data); + } catch (error) { + reject(new Error('Invalid JSON file')); + } + }; + reader.onerror = () => reject(new Error('File reading error')); + reader.readAsText(file); + } else { + reject(new Error('No file selected')); + } + }; + input.click(); + }), + + isWhitePixel: (r, g, b) => { + const wt = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD; + return r >= wt && g >= wt && b >= wt; + }, + + resolveColor(targetRgb, availableColors) { + if (!availableColors || availableColors.length === 0) { + return { id: null, rgb: targetRgb }; + } + + let bestId = availableColors[0].id; + let bestRgb = [...availableColors[0].rgb]; + let bestScore = Infinity; + + for (let i = 0; i < availableColors.length; i++) { + const c = availableColors[i]; + const [r, g, b] = c.rgb; + const rmean = (r + targetRgb[0]) / 2; + const rdiff = r - targetRgb[0]; + const gdiff = g - targetRgb[1]; + const bdiff = b - targetRgb[2]; + const dist = Math.sqrt( + (((512 + rmean) * rdiff * rdiff) >> 8) + + 4 * gdiff * gdiff + + (((767 - rmean) * bdiff * bdiff) >> 8) + ); + if (dist < bestScore) { + bestScore = dist; + bestId = c.id; + bestRgb = [...c.rgb]; + if (dist === 0) break; + } + } + + return { id: bestId, rgb: bestRgb }; + }, + + calculateTileRange( + startRegionX, + startRegionY, + startPixelX, + startPixelY, + width, + height, + tileSize = 1000 + ) { + const endPixelX = startPixelX + width; + const endPixelY = startPixelY + height; + + return { + startTileX: startRegionX + Math.floor(startPixelX / tileSize), + startTileY: startRegionY + Math.floor(startPixelY / tileSize), + endTileX: startRegionX + Math.floor((endPixelX - 1) / tileSize), + endTileY: startRegionY + Math.floor((endPixelY - 1) / tileSize), + }; + }, + + dynamicSleep: async function (tickAndGetRemainingMs) { + let remaining = Math.max(0, await tickAndGetRemainingMs()); + while (remaining > 0) { + const interval = remaining > 5000 ? 2000 : remaining > 1000 ? 500 : 100; + await this.sleep(Math.min(interval, remaining)); + remaining = Math.max(0, await tickAndGetRemainingMs()); + } + }, + + // NUEVA IMPLEMENTACIÓN TURNSTILE COMPLETA - ACTUALIZADA DESDE new.txt + turnstileLoaded: false, + _turnstileContainer: null, + _turnstileOverlay: null, + _turnstileWidgetId: null, + _lastSitekey: null, + _cachedSitekey: null, + + async loadTurnstile() { + if (window.turnstile) { + this.turnstileLoaded = true; + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + if (document.querySelector('script[src^="https://challenges.cloudflare.com/turnstile/v0/api.js"]')) { + const checkReady = () => { + if (window.turnstile) { + this.turnstileLoaded = true; + resolve(); + } else { + setTimeout(checkReady, 100); + } + }; + return checkReady(); + } + + const script = document.createElement('script'); + script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; + script.async = true; + script.defer = true; + script.onload = () => { + this.turnstileLoaded = true; + Utils.addDebugLog('Turnstile script loaded successfully', 'success'); + resolve(); + }; + script.onerror = () => { + Utils.addDebugLog('Failed to load Turnstile script', 'error'); + reject(new Error('Failed to load Turnstile')); + }; + document.head.appendChild(script); + }); + }, + + ensureTurnstileContainer() { + if (!this._turnstileContainer || !document.body.contains(this._turnstileContainer)) { + if (this._turnstileContainer) { + this._turnstileContainer.remove(); + } + + this._turnstileContainer = document.createElement('div'); + this._turnstileContainer.className = 'wplace-turnstile-hidden'; + this._turnstileContainer.setAttribute('aria-hidden', 'true'); + this._turnstileContainer.id = 'turnstile-widget-container'; + document.body.appendChild(this._turnstileContainer); + } + return this._turnstileContainer; + }, + + ensureTurnstileOverlayContainer() { + if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { + return this._turnstileOverlay; + } + + const overlay = document.createElement('div'); + overlay.id = 'turnstile-overlay-container'; + overlay.className = 'wplace-turnstile-overlay wplace-overlay-hidden'; + + const title = document.createElement('div'); + title.textContent = Utils.t('turnstileInstructions'); + title.className = 'wplace-turnstile-title'; + + const host = document.createElement('div'); + host.id = 'turnstile-overlay-host'; + host.className = 'wplace-turnstile-host'; + + const hideBtn = document.createElement('button'); + hideBtn.textContent = Utils.t('hideTurnstileBtn'); + hideBtn.className = 'wplace-turnstile-hide-btn'; + hideBtn.addEventListener('click', () => overlay.remove()); + + overlay.appendChild(title); + overlay.appendChild(host); + overlay.appendChild(hideBtn); + document.body.appendChild(overlay); + + this._turnstileOverlay = overlay; + return overlay; + }, + + async executeTurnstile(sitekey, action = 'paint') { + await this.loadTurnstile(); + + // Try reusing existing widget first if sitekey matches + if (this._turnstileWidgetId && this._lastSitekey === sitekey && window.turnstile?.execute) { + try { + console.log('🔄 Reusing existing Turnstile widget...'); + const token = await Promise.race([ + window.turnstile.execute(this._turnstileWidgetId, { action }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Execute timeout')), 15000) + ), + ]); + if (token && token.length > 20) { + console.log('✅ Token generated via widget reuse'); + return token; + } + } catch (error) { + console.log(' Widget reuse failed, will create a fresh widget:', error.message); + } + } + + // Try invisible widget first + const invisibleToken = await this.createTurnstileWidget(sitekey, action); + if (invisibleToken && invisibleToken.length > 20) { + return invisibleToken; + } + + console.log(' Falling back to interactive Turnstile (visible).'); + return await this.createTurnstileWidgetInteractive(sitekey, action); + }, + + async createTurnstileWidget(sitekey, action) { + return new Promise((resolve) => { + try { + // Force cleanup of any existing widget + if (this._turnstileWidgetId && window.turnstile?.remove) { + try { + window.turnstile.remove(this._turnstileWidgetId); + console.log('🧹 Cleaned up existing Turnstile widget'); + } catch (e) { + console.warn('⚠️ Widget cleanup warning:', e.message); + } + } + + const container = this.ensureTurnstileContainer(); + container.innerHTML = ''; + + // Verify Turnstile is available + if (!window.turnstile?.render) { + console.error('❌ Turnstile not available for rendering'); + resolve(null); + return; + } + + console.log('🔧 Creating invisible Turnstile widget...'); + const widgetId = window.turnstile.render(container, { + sitekey, + action, + size: 'invisible', + retry: 'auto', + 'retry-interval': 8000, + callback: (token) => { + console.log('✅ Invisible Turnstile callback'); + resolve(token); + }, + 'error-callback': () => resolve(null), + 'timeout-callback': () => resolve(null), + }); + + this._turnstileWidgetId = widgetId; + this._lastSitekey = sitekey; + + if (!widgetId) { + return resolve(null); + } + + // Execute the widget and race with timeout + Promise.race([ + window.turnstile.execute(widgetId, { action }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Invisible execute timeout')), 12000) + ), + ]) + .then(resolve) + .catch(() => resolve(null)); + } catch (e) { + console.error('❌ Invisible Turnstile creation failed:', e); + resolve(null); + } + }); + }, + + async createTurnstileWidgetInteractive(sitekey, action) { + // Create a visible widget that users can interact with if needed + console.log('🔄 Creating interactive Turnstile widget (visible)'); + + return new Promise((resolve) => { + try { + // Force cleanup of any existing widget + if (this._turnstileWidgetId && window.turnstile?.remove) { + try { + window.turnstile.remove(this._turnstileWidgetId); + } catch (e) { + console.warn('⚠️ Widget cleanup warning:', e.message); + } + } + + const overlay = this.ensureTurnstileOverlayContainer(); + overlay.classList.remove('wplace-overlay-hidden'); + overlay.style.display = 'block'; + + const host = overlay.querySelector('#turnstile-overlay-host'); + host.innerHTML = ''; + + // Set a timeout for interactive mode + const timeout = setTimeout(() => { + console.warn('⏰ Interactive Turnstile widget timeout'); + overlay.classList.add('wplace-overlay-hidden'); + overlay.style.display = 'none'; + resolve(null); + }, 60000); // 60 seconds for user interaction + + const widgetId = window.turnstile.render(host, { + sitekey, + action, + size: 'normal', + theme: 'light', + callback: (token) => { + clearTimeout(timeout); + overlay.classList.add('wplace-overlay-hidden'); + overlay.style.display = 'none'; + console.log('✅ Interactive Turnstile completed successfully'); + + if (typeof token === 'string' && token.length > 20) { + resolve(token); + } else { + console.warn('❌ Invalid token from interactive widget'); + resolve(null); + } + }, + 'error-callback': (error) => { + clearTimeout(timeout); + overlay.classList.add('wplace-overlay-hidden'); + overlay.style.display = 'none'; + console.warn('❌ Interactive Turnstile error:', error); + resolve(null); + }, + }); + + this._turnstileWidgetId = widgetId; + this._lastSitekey = sitekey; + + if (!widgetId) { + clearTimeout(timeout); + overlay.classList.add('wplace-overlay-hidden'); + overlay.style.display = 'none'; + console.warn('❌ Failed to create interactive Turnstile widget'); + resolve(null); + } else { + console.log('✅ Interactive Turnstile widget created, waiting for user interaction...'); + } + } catch (e) { + console.error('❌ Interactive Turnstile creation failed:', e); + resolve(null); + } + }); + }, + + cleanupTurnstile() { + if (this._turnstileWidgetId && window.turnstile?.remove) { + try { + window.turnstile.remove(this._turnstileWidgetId); + } catch (e) { + console.warn('Failed to cleanup Turnstile widget:', e); + } + } + + if (this._turnstileContainer && document.body.contains(this._turnstileContainer)) { + this._turnstileContainer.remove(); + } + + if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { + this._turnstileOverlay.remove(); + } + + this._turnstileWidgetId = null; + this._turnstileContainer = null; + this._turnstileOverlay = null; + this._lastSitekey = null; + }, + + // DETECCIÓN DE SITEKEY MEJORADA - ACTUALIZADA DESDE new.txt + async obtainSitekeyAndToken(fallback = '0x4AAAAAABpqJe8FO0N84q0F') { + // Cache sitekey to avoid repeated DOM queries + if (this._cachedSitekey) { + console.log('🔍 Using cached sitekey:', this._cachedSitekey); + + return isTokenValid() + ? { + sitekey: this._cachedSitekey, + token: turnstileToken, + } + : { sitekey: this._cachedSitekey, token: null }; + } + + // List of potential sitekeys to try + const potentialSitekeys = [ + '0x4AAAAAABpqJe8FO0N84q0F', // WPlace common sitekey + '0x4AAAAAAAJ7xjKAp6Mt_7zw', // Alternative WPlace sitekey + '0x4AAAAAADm5QWx6Ov2LNF2g', // Another common sitekey + ]; + const trySitekey = async (sitekey, source) => { + if (!sitekey || sitekey.length < 10) return null; + + console.log(`🔍 Testing sitekey from ${source}:`, sitekey); + const token = await this.executeTurnstile(sitekey); + + if (token && token.length >= 20) { + console.log(`✅ Valid token generated from ${source} sitekey`); + setTurnstileToken(token); + this._cachedSitekey = sitekey; + return { sitekey, token }; + } else { + console.log(`❌ Failed to get token from ${source} sitekey`); + return null; + } + }; + + try { + // 1️⃣ data-sitekey attribute + const sitekeySel = document.querySelector('[data-sitekey]'); + if (sitekeySel) { + const sitekey = sitekeySel.getAttribute('data-sitekey'); + const result = await trySitekey(sitekey, 'data attribute'); + if (result) { + return result; + } + } + + // 2️⃣ Turnstile element + const turnstileEl = document.querySelector('.cf-turnstile'); + if (turnstileEl?.dataset?.sitekey) { + const sitekey = turnstileEl.dataset.sitekey; + const result = await trySitekey(sitekey, 'turnstile element'); + if (result) { + return result; + } + } + + // 3️⃣ Meta tags + const metaTags = document.querySelectorAll( + 'meta[name*="turnstile"], meta[property*="turnstile"]' + ); + for (const meta of metaTags) { + const content = meta.getAttribute('content'); + const result = await trySitekey(content, 'meta tag'); + if (result) { + return result; + } + } + + // 4️⃣ Global variable + if (window.__TURNSTILE_SITEKEY) { + const result = await trySitekey(window.__TURNSTILE_SITEKEY, 'global variable'); + if (result) { + return result; + } + } + + // 5️⃣ Script tags + const scripts = document.querySelectorAll('script'); + for (const script of scripts) { + const content = script.textContent || script.innerHTML; + const match = content.match( + /(?:sitekey|data-sitekey)['"\s\[\]:\=\(]*['"]?([0-9a-zA-Z_-]{20,})['"]?/i + ); + if (match && match[1]) { + const extracted = match[1].replace(/['"]/g, ''); + const result = await trySitekey(extracted, 'script content'); + if (result) { + return result; + } + } + } + + // 6️⃣ Known potential sitekeys + console.log('🔍 Testing known potential sitekeys...'); + for (const testSitekey of potentialSitekeys) { + const result = await trySitekey(testSitekey, 'known list'); + if (result) { + return result; + } + } + } catch (error) { + console.warn('⚠️ Error during sitekey detection:', error); + } + + // 7️⃣ Fallback + console.log('🔧 Trying fallback sitekey:', fallback); + const fallbackResult = await trySitekey(fallback, 'fallback'); + if (fallbackResult) { + return fallbackResult; + } + + console.error('❌ No working sitekey or token found.'); + return { sitekey: null, token: null }; + }, + }; + + // Enhanced Overlay Manager for pixel detection with autonomous capabilities + class OverlayManager { + constructor() { + this.originalTiles = new Map(); + this.originalTilesData = new Map(); + this.tileSize = 1000; + this.loadingPromises = new Map(); + this.autonomousMode = state.autonomousMode; + } + + async processAndRespondToTileRequest(eventData) { + const { endpoint, blobID, blobData } = eventData; + + const tileMatch = endpoint.match(/(\d+)\/(\d+)\.png/); + if (tileMatch) { + const tileX = parseInt(tileMatch[1], 10); + const tileY = parseInt(tileMatch[2], 10); + const tileKey = `${tileX},${tileY}`; + + try { + const originalBitmap = await createImageBitmap(blobData); + this.originalTiles.set(tileKey, originalBitmap); + + try { + let canvas, ctx; + if (typeof OffscreenCanvas !== 'undefined') { + canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height); + ctx = canvas.getContext('2d'); + } else { + canvas = document.createElement('canvas'); + canvas.width = originalBitmap.width; + canvas.height = originalBitmap.height; + ctx = canvas.getContext('2d'); + } + ctx.imageSmoothingEnabled = false; + ctx.drawImage(originalBitmap, 0, 0); + const imgData = ctx.getImageData(0, 0, originalBitmap.width, originalBitmap.height); + + this.originalTilesData.set(tileKey, { + w: originalBitmap.width, + h: originalBitmap.height, + data: new Uint8ClampedArray(imgData.data), + }); + + if (this.autonomousMode) { + Utils.addDebugLog(`Auto-cached tile: ${tileKey} (${originalBitmap.width}x${originalBitmap.height})`, 'info'); + } + } catch (e) { + Utils.addDebugLog(`Failed to cache tile ImageData: ${tileKey} - ${e.message}`, 'warning'); + } + } catch (e) { + Utils.addDebugLog(`Failed to create tile bitmap: ${tileKey} - ${e.message}`, 'error'); + } + } + + window.postMessage( + { + source: 'auto-image-overlay', + blobID: blobID, + blobData: blobData, + }, + '*' + ); + } + + async getTilePixelColor(tileX, tileY, pixelX, pixelY) { + const tileKey = `${tileX},${tileY}`; + const alphaThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + + const cached = this.originalTilesData.get(tileKey); + if (cached && cached.data && cached.w > 0 && cached.h > 0) { + const x = Math.max(0, Math.min(cached.w - 1, pixelX)); + const y = Math.max(0, Math.min(cached.h - 1, pixelY)); + const idx = (y * cached.w + x) * 4; + const d = cached.data; + const a = d[idx + 3]; + + if (!state.paintTransparentPixels && a < alphaThresh) { + return null; + } + return [d[idx], d[idx + 1], d[idx + 2], a]; + } + + const bitmap = this.originalTiles.get(tileKey); + if (!bitmap) { + if (this.autonomousMode) { + Utils.addDebugLog(`Tile ${tileKey} not available, requesting load...`, 'warning'); + } + return null; + } + + try { + let canvas, ctx; + if (typeof OffscreenCanvas !== 'undefined') { + canvas = new OffscreenCanvas(bitmap.width, bitmap.height); + ctx = canvas.getContext('2d'); + } else { + canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + ctx = canvas.getContext('2d'); + } + ctx.imageSmoothingEnabled = false; + ctx.drawImage(bitmap, 0, 0); + + const x = Math.max(0, Math.min(bitmap.width - 1, pixelX)); + const y = Math.max(0, Math.min(bitmap.height - 1, pixelY)); + const data = ctx.getImageData(x, y, 1, 1).data; + const a = data[3]; + + if (!state.paintTransparentPixels && a < alphaThresh) { + return null; + } + + return [data[0], data[1], data[2], a]; + } catch (e) { + Utils.addDebugLog(`Error reading pixel from tile ${tileKey}: ${e.message}`, 'error'); + return null; + } + } + + async waitForTiles(startRegionX, startRegionY, pixelWidth, pixelHeight, startPixelX = 0, startPixelY = 0, timeoutMs = 15000) { + const { startTileX, startTileY, endTileX, endTileY } = Utils.calculateTileRange( + startRegionX, + startRegionY, + startPixelX, + startPixelY, + pixelWidth, + pixelHeight, + this.tileSize + ); + + const requiredTiles = []; + for (let ty = startTileY; ty <= endTileY; ty++) { + for (let tx = startTileX; tx <= endTileX; tx++) { + requiredTiles.push(`${tx},${ty}`); + } + } + + if (requiredTiles.length === 0) return true; + + Utils.addDebugLog(`Waiting for ${requiredTiles.length} tiles (autonomous: ${this.autonomousMode})...`, 'info'); + + const startTime = Date.now(); + let lastProgress = 0; + + while (Date.now() - startTime < timeoutMs) { + if (state.stopFlag) { + Utils.addDebugLog('waitForTiles: stopped by user', 'warning'); + return false; + } + + const loaded = requiredTiles.filter((k) => this.originalTiles.has(k)).length; + const progress = Math.round((loaded / requiredTiles.length) * 100); + + if (progress !== lastProgress && progress % 20 === 0) { + Utils.addDebugLog(`Tile loading progress: ${loaded}/${requiredTiles.length} (${progress}%)`, 'info'); + lastProgress = progress; + } + + if (loaded === requiredTiles.length) { + Utils.addDebugLog(`All ${requiredTiles.length} required tiles are loaded`, 'success'); + return true; + } + + await Utils.sleep(this.autonomousMode ? 500 : 1000); + } + + const loaded = requiredTiles.filter((k) => this.originalTiles.has(k)).length; + Utils.addDebugLog(`Timeout waiting for tiles: ${loaded}/${requiredTiles.length} loaded`, 'warning'); + + if (this.autonomousMode && loaded > requiredTiles.length * 0.8) { + Utils.addDebugLog(`Autonomous mode: proceeding with ${loaded}/${requiredTiles.length} tiles (80%+ loaded)`, 'warning'); + return true; + } + + return loaded > 0; + } + } + + const overlayManager = new OverlayManager(); + + // Enhanced WPlace API Service with autonomous capabilities - ACTUALIZADO CON NUEVA LÓGICA + const WPlaceService = { + async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount = 0) { + try { + await ensureToken(); + if (!turnstileToken) { + Utils.addDebugLog('No valid token available for paint request', 'error'); + return 'token_error'; + } + + const payload = { + coords: [pixelX, pixelY], + colors: [color], + t: turnstileToken, + fp: fpStr32, + }; + + const wasmToken = await createWasmToken(regionX, regionY, payload); + if (!wasmToken) { + Utils.addDebugLog('Failed to generate WASM token', 'error'); + return false; + } + + const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { + method: 'POST', + headers: { + 'Content-Type': 'text/plain;charset=UTF-8', + 'x-pawtect-token': wasmToken + }, + credentials: 'include', + body: JSON.stringify(payload), + }); + + if (res.status === 403) { + Utils.addDebugLog(`403 Forbidden for pixel (${pixelX},${pixelY}). Token invalid/expired.`, 'error'); + invalidateToken(); + + if (retryCount < 2 && state.autonomousMode) { + Utils.addDebugLog(`Autonomous retry ${retryCount + 1}/2 for pixel (${pixelX},${pixelY})`, 'warning'); + await Utils.sleep(1000); + return await this.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount + 1); + } + + return 'token_error'; + } + + if (!res.ok) { + Utils.addDebugLog(`Paint request failed with status ${res.status}`, 'error'); + return false; + } + + const data = await res.json(); + const success = data?.painted === 1; + + if (success) { + Utils.addDebugLog(`Paint SUCCESS for (${pixelX},${pixelY}) with color ${color}`, 'success'); + } else { + Utils.addDebugLog(`Paint FAILED for (${pixelX},${pixelY}) - server response: ${JSON.stringify(data)}`, 'error'); + } + + return success; + } catch (e) { + Utils.addDebugLog(`Paint request error for (${pixelX},${pixelY}): ${e.message}`, 'error'); + return false; + } + }, + + async getCharges() { + try { + const res = await fetch('https://backend.wplace.live/me', { + credentials: 'include', + }); + if (!res.ok) return { charges: 0, max: 1, cooldown: CONFIG.COOLDOWN_DEFAULT }; + const data = await res.json(); + return { + charges: data.charges?.count ?? 0, + max: data.charges?.max ?? 1, + cooldown: data.charges?.cooldownMs ?? CONFIG.COOLDOWN_DEFAULT, + }; + } catch (e) { + Utils.addDebugLog(`Error fetching charges: ${e.message}`, 'warning'); + return { charges: 0, max: 1, cooldown: CONFIG.COOLDOWN_DEFAULT }; + } + }, + }; + + // Anti-grief repair system with autonomous capabilities + async function scanForDamage() { + if (!state.imageData || !state.startPosition || !state.region) { + Utils.addDebugLog('No image data or position for scanning', 'warning'); + return []; + } + + Utils.addDebugLog('Starting autonomous damage scan...', 'info'); + updateStatus(Utils.t('scanningForDamage')); + + const damagedPixels = []; + const { width, height, pixels } = state.imageData; + + const ready = await overlayManager.waitForTiles( + state.region.x, + state.region.y, + width, + height, + state.startPosition.x, + state.startPosition.y, + state.autonomousMode ? 20000 : 15000 + ); + + if (!ready) { + Utils.addDebugLog('Failed to load required tiles for scanning', 'error'); + if (state.autonomousMode) { + Utils.addDebugLog('Autonomous mode: will retry scan in 30 seconds', 'warning'); + setTimeout(() => { + if (state.autoRepairEnabled && !state.stopFlag) { + scanForDamage(); + } + }, 30000); + } + return []; + } + + Utils.addDebugLog(`Scanning ${width}x${height} image for damage (autonomous: ${state.autonomousMode})...`, 'info'); + + let scannedPixels = 0; + let lastProgressUpdate = Date.now(); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + if (state.stopFlag) break; + + const idx = (y * width + x) * 4; + const originalR = pixels[idx]; + const originalG = pixels[idx + 1]; + const originalB = pixels[idx + 2]; + const originalA = pixels[idx + 3]; + + if (!state.paintTransparentPixels && originalA < CONFIG.TRANSPARENCY_THRESHOLD) { + continue; + } + + if (!state.paintWhitePixels && Utils.isWhitePixel(originalR, originalG, originalB)) { + continue; + } + + scannedPixels++; + + const absX = state.startPosition.x + x; + const absY = state.startPosition.y + y; + const tileX = state.region.x + Math.floor(absX / 1000); + const tileY = state.region.y + Math.floor(absY / 1000); + const pixelX = absX % 1000; + const pixelY = absY % 1000; + + try { + const currentPixel = await overlayManager.getTilePixelColor(tileX, tileY, pixelX, pixelY); + + if (!currentPixel) { + if (!state.autonomousMode) { + Utils.addDebugLog(`No current pixel data for (${x},${y}) - tile ${tileX},${tileY}`, 'warning'); + } + continue; + } + + const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); + const currentColor = Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors); + + if (targetColor.id !== currentColor.id) { + damagedPixels.push({ + x, + y, + originalColor: targetColor, + currentColor: currentColor, + originalRgb: [originalR, originalG, originalB], + currentRgb: currentPixel.slice(0, 3) + }); + + if (!state.autonomousMode || damagedPixels.length <= 10) { + Utils.addDebugLog(`Damage at (${x},${y}): expected color ${targetColor.id}, found color ${currentColor.id}`, 'warning'); + } + } + } catch (e) { + if (!state.autonomousMode) { + Utils.addDebugLog(`Error checking pixel (${x},${y}): ${e.message}`, 'error'); + } + } + } + + if (state.autonomousMode && Date.now() - lastProgressUpdate > 5000) { + Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%), found ${damagedPixels.length} damaged`, 'info'); + lastProgressUpdate = Date.now(); + } else if (!state.autonomousMode && y % 10 === 0) { + Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%)`, 'info'); + } + } + + const logLevel = damagedPixels.length > 0 ? 'warning' : 'success'; + Utils.addDebugLog(`Scan complete. Checked ${scannedPixels} pixels, found ${damagedPixels.length} damaged`, logLevel); + + return damagedPixels; + } + + async function repairDamagedPixels(damagedPixels) { + if (damagedPixels.length === 0) { + updateStatus(Utils.t('noDamageDetected')); + return 0; + } + + Utils.addDebugLog(`Starting autonomous repair of ${damagedPixels.length} pixels`, 'info'); + updateStatus(Utils.t('repairingPixels', { count: damagedPixels.length })); + + let repairedCount = 0; + let consecutiveFailures = 0; + const maxConsecutiveFailures = state.autonomousMode ? 5 : 3; + + for (let i = 0; i < damagedPixels.length; i++) { + const pixel = damagedPixels[i]; + + if (state.stopFlag) { + Utils.addDebugLog('Repair stopped by user request', 'warning'); + break; + } + + // Enhanced charge management for autonomous mode + await updateCharges(); + let chargeWaitAttempts = 0; + const maxChargeWaitAttempts = state.autonomousMode ? 20 : 10; + + while (state.displayCharges < 1 && !state.stopFlag && chargeWaitAttempts < maxChargeWaitAttempts) { + chargeWaitAttempts++; + const waitTime = state.autonomousMode ? Math.min(state.cooldown, 10000) : state.cooldown; + + if (chargeWaitAttempts === 1) { + Utils.addDebugLog(`Waiting for charges... (${state.displayCharges}/${state.maxCharges})`, 'info'); + } + + await Utils.dynamicSleep(() => { + if (state.displayCharges >= 1) return 0; + if (state.stopFlag) return 0; + return waitTime; + }); + await updateCharges(); + } + + if (state.stopFlag) break; + + if (state.displayCharges < 1) { + Utils.addDebugLog(`No charges available after waiting, skipping pixel (${pixel.x},${pixel.y})`, 'warning'); + continue; + } + + const success = await repairSinglePixel(pixel); + if (success) { + repairedCount++; + consecutiveFailures = 0; + if (!state.autonomousMode || repairedCount <= 10 || repairedCount % 10 === 0) { + Utils.addDebugLog(`Repaired pixel (${pixel.x},${pixel.y}) with color ${pixel.originalColor.id} [${repairedCount}/${damagedPixels.length}]`, 'success'); + } + } else { + consecutiveFailures++; + Utils.addDebugLog(`Failed to repair pixel (${pixel.x},${pixel.y}) [${consecutiveFailures}/${maxConsecutiveFailures} consecutive failures]`, 'error'); + + if (consecutiveFailures >= maxConsecutiveFailures) { + Utils.addDebugLog(`Too many consecutive failures (${consecutiveFailures}), pausing repair`, 'error'); + if (state.autonomousMode) { + Utils.addDebugLog('Autonomous mode: will retry repair in 60 seconds', 'warning'); + setTimeout(() => { + if (state.autoRepairEnabled && !state.stopFlag) { + performRepairCheck(); + } + }, 60000); + } + break; + } + } + + await updateCharges(); + + // Dynamic delay based on mode and success rate + const baseDelay = state.autonomousMode ? 100 : 200; + const adaptiveDelay = consecutiveFailures > 0 ? baseDelay * (consecutiveFailures + 1) : baseDelay; + await Utils.sleep(adaptiveDelay); + } + + const message = Utils.t('repairComplete', { repaired: repairedCount }); + updateStatus(message); + Utils.addDebugLog(`${message} (${damagedPixels.length - repairedCount} remaining)`, 'success'); + + return repairedCount; + } + + async function repairSinglePixel(pixel) { + const { x, y, originalColor } = pixel; + + const absX = state.startPosition.x + x; + const absY = state.startPosition.y + y; + const regionX = state.region.x + Math.floor(absX / 1000); + const regionY = state.region.y + Math.floor(absY / 1000); + const pixelX = absX % 1000; + const pixelY = absY % 1000; + + try { + const result = await WPlaceService.paintPixelInRegion( + regionX, + regionY, + pixelX, + pixelY, + originalColor.id + ); + + if (result === 'token_error') { + Utils.addDebugLog('Token error during repair, refreshing token...', 'warning'); + await ensureToken(true); + await Utils.sleep(state.autonomousMode ? 500 : 1000); + + // Retry once with new token + const retryResult = await WPlaceService.paintPixelInRegion(regionX, regionY, pixelX, pixelY, originalColor.id); + return retryResult === true; + } + + return result === true; + } catch (e) { + Utils.addDebugLog(`Error repairing pixel (${x},${y}): ${e.message}`, 'error'); + return false; + } + } + + async function performRepairCheck() { + if (state.running) { + Utils.addDebugLog('Repair check skipped - manual repair in progress', 'info'); + return; + } + + try { + Utils.addDebugLog('Autonomous repair check triggered', 'info'); + + // Ensure we have a valid token before starting + if (!isTokenValid()) { + Utils.addDebugLog('No valid token for autonomous repair, generating...', 'warning'); + await ensureToken(true); + if (!isTokenValid()) { + Utils.addDebugLog('Failed to generate token for autonomous repair, will retry next cycle', 'error'); + return; + } + } + + const damagedPixels = await scanForDamage(); + + if (damagedPixels.length > 0) { + Utils.addDebugLog(`Autonomous repair: Found ${damagedPixels.length} damaged pixels, starting repair`, 'warning'); + updateStatus(Utils.t('damageDetected', { count: damagedPixels.length })); + + // Show attack notification + showAttackNotification('attack', damagedPixels.length); + state.lastAttackState = 'attacked'; + + const repairedCount = await repairDamagedPixels(damagedPixels); + + // Show repair complete notification if all pixels were repaired + if (repairedCount === damagedPixels.length) { + showAttackNotification('repaired'); + state.lastAttackState = 'repaired'; + } + } else { + updateStatus(Utils.t('noDamageDetected')); + + // Show peaceful state if we're not under attack + if (state.lastAttackState !== 'peaceful') { + showPeacefulState(); + state.lastAttackState = 'peaceful'; + } + + if (!state.autonomousMode) { + Utils.addDebugLog('Autonomous repair: No damage detected', 'success'); + } + } + } catch (error) { + Utils.addDebugLog(`Autonomous repair error: ${error.message}`, 'error'); + + if (state.autonomousMode) { + Utils.addDebugLog('Autonomous mode: will retry repair check in 120 seconds due to error', 'warning'); + setTimeout(() => { + if (state.autoRepairEnabled && !state.stopFlag) { + performRepairCheck(); + } + }, 120000); + } + } + } + + function startAutoRepair() { + if (state.autoRepairTimer) { + clearInterval(state.autoRepairTimer); + } + + const intervalMs = state.autoRepairInterval * 1000; + state.autoRepairTimer = setInterval(performRepairCheck, intervalMs); + + const message = Utils.t('autoRepairStarted', { interval: state.autoRepairInterval }); + Utils.addDebugLog(`${message} (autonomous: ${state.autonomousMode})`, 'info'); + Utils.showAlert(message, 'success'); + + // Perform first check immediately + setTimeout(performRepairCheck, 2000); + } + + function stopAutoRepair() { + if (state.autoRepairTimer) { + clearInterval(state.autoRepairTimer); + state.autoRepairTimer = null; + } + + const message = Utils.t('autoRepairStopped'); + Utils.addDebugLog(`${message}`, 'info'); + Utils.showAlert(message, 'info'); + } + + // FUNCIONES DE UI MEJORADAS CON GESTIÓN DE CONFIGURACIÓN INICIAL + function enableFileOperations() { + state.initialSetupComplete = true; + + const loadBtn = document.querySelector('#loadFileBtn'); + if (loadBtn) { + loadBtn.disabled = false; + loadBtn.title = ''; + loadBtn.style.animation = 'pulse 0.6s ease-in-out'; + setTimeout(() => { + if (loadBtn) loadBtn.style.animation = ''; + }, 600); + Utils.addDebugLog('Load Progress button enabled after initial setup', 'success'); + } + + Utils.showAlert(Utils.t('fileOperationsAvailable'), 'success'); + } + + async function initializeTokenGenerator() { + if (isTokenValid()) { + Utils.addDebugLog('Valid token already available, skipping initialization', 'success'); + updateTokenStatus(); + enableFileOperations(); + return; + } + + try { + Utils.addDebugLog('Initializing Turnstile token generator...', 'info'); + updateStatus(Utils.t('initializingToken')); + + await Utils.loadTurnstile(); + Utils.addDebugLog('Turnstile script loaded successfully', 'success'); + + if (state.autoTokenRefresh) { + Utils.addDebugLog('Pre-generating token for autonomous operations...', 'info'); + await ensureToken(); + } + + state.initialSetupComplete = true; + Utils.addDebugLog('Enhanced token generator initialization complete', 'success'); + updateStatus(Utils.t('tokenReady')); + enableFileOperations(); + + } catch (error) { + Utils.addDebugLog('Token system initialization failed: ' + error.message, 'warning'); + Utils.addDebugLog('Manual token generation will be required', 'info'); + enableFileOperations(); + } + } + + function updateStatus(message) { + const statusEl = document.getElementById('status'); + if (statusEl) { + if (typeof message === 'string') { + statusEl.innerHTML = message; + } else { + statusEl.textContent = message; + } + } + } + + function updateDebugConsole() { + const debugConsole = document.getElementById('debugConsole'); + if (!debugConsole) return; + + const logsHtml = state.debugLogs.map(log => { + const colorClass = log.type === 'error' ? 'neon-text danger' : + log.type === 'warning' ? 'neon-text warning' : + log.type === 'success' ? 'neon-text success' : + log.type === 'info' ? 'neon-text info' : 'neon-text'; + + return `
+ [${log.timestamp}] ${log.message} +
`; + }).join(''); + + debugConsole.innerHTML = logsHtml; + debugConsole.scrollTop = debugConsole.scrollHeight; + } + + async function updateCharges() { + try { + const { charges, max, cooldown } = await WPlaceService.getCharges(); + state.displayCharges = Math.floor(charges); + state.maxCharges = Math.max(1, Math.floor(max)); + state.cooldown = cooldown; + + updateChargesDisplay(); + } catch (error) { + if (!state.autonomousMode) { + Utils.addDebugLog(`Error updating charges: ${error.message}`, 'error'); + } + } + } + + function updateChargesDisplay() { + const chargesEl = document.getElementById('chargesInfo'); + if (chargesEl) { + chargesEl.textContent = `Charges: ${state.displayCharges}/${state.maxCharges} (cooldown: ${Math.round(state.cooldown / 1000)}s)`; + } + } + + function updateTokenStatus() { + const tokenEl = document.getElementById('tokenInfo'); + if (tokenEl) { + // Remove existing classes first + tokenEl.className = ''; + + if (isTokenValid()) { + const remaining = Math.round((tokenExpiryTime - Date.now()) / 1000); + tokenEl.textContent = `Token: Valid (expires in ${remaining}s)`; + tokenEl.className = 'neon-text success'; + } else { + tokenEl.textContent = 'Token: Not generated or expired'; + tokenEl.className = 'neon-text warning'; + } + } + } + + function updateSystemStatus() { + const systemEl = document.getElementById('systemInfo'); + if (systemEl) { + const autonomousStatus = state.autonomousMode ? 'AUTONOMOUS' : 'MANUAL'; + const tokenMode = state.tokenSource.toUpperCase(); + systemEl.textContent = `System: ${autonomousStatus} | Token: ${tokenMode} | Auto-refresh: ${state.autoTokenRefresh ? 'ON' : 'OFF'}`; + } + } + + // Window dragging functionality + function makeWindowDraggable() { + const container = document.getElementById('wplace-repair-tool'); + const header = document.getElementById('window-header'); + + let isDragging = false; + let currentX; + let currentY; + let initialX; + let initialY; + let xOffset = 0; + let yOffset = 0; + + function dragStart(e) { + if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') { + return; + } + + initialX = e.clientX - xOffset; + initialY = e.clientY - yOffset; + + if (e.target === header || header.contains(e.target)) { + isDragging = true; + container.style.cursor = 'grabbing'; + } + } + + function dragEnd() { + initialX = currentX; + initialY = currentY; + isDragging = false; + container.style.cursor = 'default'; + } + + function drag(e) { + if (isDragging) { + e.preventDefault(); + currentX = e.clientX - initialX; + currentY = e.clientY - initialY; + xOffset = currentX; + yOffset = currentY; + + container.style.transform = `translate(${currentX}px, ${currentY}px)`; + } + } + + header.addEventListener('mousedown', dragStart); + document.addEventListener('mouseup', dragEnd); + document.addEventListener('mousemove', drag); + } + + // Enhanced UI Creation with NEW IMPLEMENTATION + function createUI() { + const existing = document.getElementById('wplace-repair-tool'); + if (existing) existing.remove(); + + const container = document.createElement('div'); + container.id = 'wplace-repair-tool'; + container.style.cssText = ` + position: fixed; top: 20px; left: 20px; z-index: 10000; + background: #1a1a2e; + border: 3px solid #00ff41; + border-radius: 0; + min-width: 600px; max-width: 700px; + box-shadow: + 0 0 30px rgb(0 255 65 / 50%), + inset 0 0 30px rgb(0 255 65 / 10%), + 0 0 0 1px #00ff41; + color: #00ff41; + font-family: 'Press Start 2P', monospace, 'Courier New'; + resize: both; overflow: hidden; + animation: neon-pulse 2s ease-in-out infinite alternate; + `; + + // Add Neon theme styles + const neonStyles = document.createElement('style'); + neonStyles.id = 'auto-repair-neon-styles'; + neonStyles.textContent = ` + @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); + + #wplace-repair-tool::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, #00ff41, transparent); + z-index: 1; + pointer-events: none; + animation: scanline 3s linear infinite; + opacity: 0.7; + } + + .neon-btn { + background: #16213e !important; + border: 2px solid #00ff41 !important; + border-radius: 0 !important; + color: #00ff41 !important; + padding: 8px 15px !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 8px !important; + text-transform: uppercase !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + text-shadow: 0 0 8px #00ff41 !important; + letter-spacing: 1px !important; + margin: 4px !important; + } + + .neon-btn:hover { + background: rgba(0,255,65,0.1) !important; + box-shadow: 0 0 20px rgb(0 255 65 / 60%) !important; + animation: pixel-blink 0.5s infinite !important; + } + + .neon-btn:disabled { + background: #0d1117 !important; + border-color: #555 !important; + color: #555 !important; + text-shadow: none !important; + cursor: not-allowed !important; + } + + .neon-btn.danger { + border-color: #ff073a !important; + color: #ff073a !important; + text-shadow: 0 0 8px #ff073a !important; + } + + .neon-btn.danger:hover { + background: rgba(255, 7, 58, 0.1) !important; + box-shadow: 0 0 20px rgb(255 7 58 / 60%) !important; + } + + .neon-btn.warning { + border-color: #ff6b35 !important; + color: #ff6b35 !important; + text-shadow: 0 0 8px #ff6b35 !important; + } + + .neon-btn.warning:hover { + background: rgba(255, 107, 53, 0.1) !important; + box-shadow: 0 0 20px rgb(255 107 53 / 60%) !important; + } + + .neon-input { + background: #16213e !important; + border: 2px solid #00ff41 !important; + border-radius: 0 !important; + color: #00ff41 !important; + padding: 6px 10px !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 8px !important; + text-shadow: 0 0 5px #00ff41 !important; + } + + .neon-input:focus { + outline: none !important; + box-shadow: 0 0 15px rgb(0 255 65 / 60%) !important; + animation: pixel-blink 0.5s infinite !important; + } + + .neon-select { + background: #16213e !important; + border: 2px solid #00ff41 !important; + border-radius: 0 !important; + color: #00ff41 !important; + padding: 4px 8px !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 7px !important; + text-shadow: 0 0 5px #00ff41 !important; + } + + .neon-checkbox { + accent-color: #00ff41 !important; + transform: scale(1.2) !important; + margin-right: 8px !important; + } + + .neon-panel { + background: rgba(22, 33, 62, 0.3) !important; + border: 1px solid #00ff41 !important; + border-radius: 0 !important; + padding: 15px !important; + margin: 10px 0 !important; + box-shadow: inset 0 0 15px rgba(0, 255, 65, 0.1) !important; + } + + .neon-text { + color: #00ff41 !important; + text-shadow: 0 0 5px #00ff41 !important; + font-family: 'Press Start 2P', monospace !important; + } + + .neon-text.success { + color: #39ff14 !important; + text-shadow: 0 0 5px #39ff14 !important; + } + + .neon-text.warning { + color: #ff6b35 !important; + text-shadow: 0 0 5px #ff6b35 !important; + } + + .neon-text.danger { + color: #ff073a !important; + text-shadow: 0 0 5px #ff073a !important; + } + + .neon-text.info { + color: #00ccff !important; + text-shadow: 0 0 5px #00ccff !important; + } + + /* Custom scrollbar for debug console */ + #debugConsole::-webkit-scrollbar { + width: 12px; + } + + #debugConsole::-webkit-scrollbar-track { + background: #16213e; + border: 1px solid #00ff41; + } + + #debugConsole::-webkit-scrollbar-thumb { + background: #00ff41; + border-radius: 0; + box-shadow: 0 0 10px #00ff41; + } + + #debugConsole::-webkit-scrollbar-thumb:hover { + background: #39ff14; + box-shadow: 0 0 15px #39ff14; + } + + /* Animations */ + @keyframes neon-pulse { + 0% { box-shadow: 0 0 30px rgb(0 255 65 / 50%), inset 0 0 30px rgb(0 255 65 / 10%), 0 0 0 1px #00ff41; } + 100% { box-shadow: 0 0 40px rgb(0 255 65 / 70%), inset 0 0 40px rgb(0 255 65 / 15%), 0 0 0 1px #00ff41; } + } + + @keyframes pixel-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.7; } + } + + @keyframes scanline { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(400px); } + } + + @keyframes text-glow { + 0% { text-shadow: 0 0 15px #00ff41; } + 100% { text-shadow: 0 0 25px #00ff41, 0 0 35px #00ff41; } + } + `; + + if (!document.getElementById('auto-repair-neon-styles')) { + document.head.appendChild(neonStyles); + } + + container.innerHTML = ` +
+
+
🔧
+

+ WPlace Autonomous Repair Tool +

+
+
+ + +
+
+ +
+
+
+ ${Utils.t('autonomousModeActive')} +
+
+ Charges: 0/1 (cooldown: 31s) +
+
+ Token: Initializing... +
+
+ System: AUTONOMOUS | Token: GENERATOR | Auto-refresh: ON +
+
+ +
+ + + + + +
+ +
+
+ +
+ +
+ + + seconds +
+ +
+ + +
+ +
+
Image: Not loaded
+
Position: Not set
+
Colors: 0 available
+
+
+ +
+
+

+ 📋 ${Utils.t('debug')} (Enhanced System) +

+ +
+ +
+
[Ready] WPlace Autonomous Repair Tool v3.0
+
[Info] NEW Turnstile implementation active
+
[Info] Enhanced token management with preloading
+
[Info] ${Utils.t('tokenSystemReady')}
+
+
+
+ `; + + document.body.appendChild(container); + makeWindowDraggable(); + setupEventListeners(); + + // Start typewriter effect + setTimeout(() => { + createTypewriterTitle(); + }, 1000); + + // Start autonomous systems + initializeAutonomousSystems(); + } + + function initializeAutonomousSystems() { + // Update charges periodically + updateCharges(); + setInterval(updateCharges, 10000); + + // Update token status + updateTokenStatus(); + setInterval(updateTokenStatus, 5000); + + // Update system status + updateSystemStatus(); + setInterval(updateSystemStatus, 15000); + + // Initialize token generator + initializeTokenGenerator(); + } + + function setupEventListeners() { + // Window controls + document.getElementById('minimizeBtn').addEventListener('click', () => { + const content = document.getElementById('window-content'); + state.windowMinimized = !state.windowMinimized; + + if (state.windowMinimized) { + content.style.display = 'none'; + document.getElementById('minimizeBtn').textContent = '+'; + } else { + content.style.display = 'block'; + document.getElementById('minimizeBtn').textContent = '−'; + } + }); + + document.getElementById('closeBtn').addEventListener('click', () => { + const container = document.getElementById('wplace-repair-tool'); + if (container) { + container.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; + container.style.opacity = '0'; + container.style.transform = 'scale(0.8)'; + setTimeout(() => { + container.remove(); + cleanup(); + }, 300); + } + }); + + // Load file + document.getElementById('loadFileBtn').addEventListener('click', async () => { + try { + Utils.addDebugLog('Loading progress file...', 'info'); + const data = await Utils.createFileUploader(); + + if (!data || !data.state || !data.imageData) { + throw new Error(Utils.t('invalidFile')); + } + + const migratedData = migrateProgressData(data); + + Object.assign(state, migratedData.state); + state.imageData = { + ...migratedData.imageData, + pixels: new Uint8ClampedArray(migratedData.imageData.pixels), + }; + + state.imageLoaded = true; + document.getElementById('repairBtn').disabled = false; + + // Update info displays + document.getElementById('imageInfo').textContent = `${state.imageData.width}x${state.imageData.height}`; + document.getElementById('positionInfo').textContent = state.startPosition ? + `(${state.startPosition.x}, ${state.startPosition.y}) in region (${state.region.x}, ${state.region.y})` : 'Not set'; + document.getElementById('colorsInfo').textContent = `${state.availableColors?.length || 0} available`; + + updateStatus(Utils.t('fileLoaded')); + Utils.addDebugLog(`Loaded image: ${state.imageData.width}x${state.imageData.height}`, 'success'); + Utils.addDebugLog(`Position: (${state.startPosition?.x}, ${state.startPosition?.y})`, 'info'); + Utils.addDebugLog(`Region: (${state.region?.x}, ${state.region?.y})`, 'info'); + Utils.addDebugLog(`Available colors: ${state.availableColors?.length || 0}`, 'info'); + + if (migratedData.state.paintWhitePixels !== undefined) { + state.paintWhitePixels = migratedData.state.paintWhitePixels; + Utils.addDebugLog(`Paint white pixels: ${state.paintWhitePixels}`, 'info'); + } + if (migratedData.state.paintTransparentPixels !== undefined) { + state.paintTransparentPixels = migratedData.state.paintTransparentPixels; + Utils.addDebugLog(`Paint transparent pixels: ${state.paintTransparentPixels}`, 'info'); + } + + Utils.showAlert(Utils.t('fileLoaded'), 'success'); + + // If autonomous mode and auto repair is enabled, start it + if (state.autonomousMode && document.getElementById('autoRepairEnabled').checked) { + Utils.addDebugLog('Autonomous mode: starting auto repair after file load', 'info'); + setTimeout(() => { + state.autoRepairEnabled = true; + startAutoRepair(); + }, 5000); + } + + } catch (error) { + Utils.addDebugLog(`Load error: ${error.message}`, 'error'); + Utils.showAlert(error.message, 'error'); + } + }); + + // Generate Token button + document.getElementById('generateTokenBtn').addEventListener('click', async () => { + const btn = document.getElementById('generateTokenBtn'); + const originalText = btn.textContent; + + try { + btn.disabled = true; + btn.textContent = '🔄 Generating...'; + btn.style.background = 'linear-gradient(135deg, #ffa726 0%, #ff9800 100%)'; + + Utils.addDebugLog('Manual token generation requested', 'info'); + + const token = await ensureToken(true); + + if (token) { + Utils.addDebugLog('Token generation succeeded!', 'success'); + Utils.showAlert('Token generated successfully!', 'success'); + updateTokenStatus(); + } else { + throw new Error('Token generation failed'); + } + } catch (error) { + Utils.addDebugLog(`Token generation failed: ${error.message}`, 'error'); + Utils.showAlert('Token generation failed', 'error'); + playNotificationSound(); + } finally { + btn.disabled = false; + btn.textContent = originalText; + btn.style.background = 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)'; + } + }); + + // Manual repair + let repairRunning = false; + document.getElementById('repairBtn').addEventListener('click', async () => { + if (!state.imageLoaded) { + Utils.showAlert('Please load a progress file first', 'warning'); + return; + } + + if (repairRunning) { + state.stopFlag = true; + Utils.addDebugLog('Manual repair stop requested', 'warning'); + return; + } + + repairRunning = true; + state.running = true; + state.stopFlag = false; + const repairBtn = document.getElementById('repairBtn'); + repairBtn.textContent = '⏸️ Stop Repair'; + repairBtn.style.background = 'linear-gradient(135deg, #ff6b35 0%, #f7931e 100%)'; + + try { + Utils.addDebugLog('Starting manual repair with enhanced autonomous system...', 'info'); + + if (!isTokenValid()) { + Utils.addDebugLog('No valid token, generating...', 'info'); + await ensureToken(true); + } + + const damagedPixels = await scanForDamage(); + + if (damagedPixels.length > 0) { + updateStatus(Utils.t('damageDetected', { count: damagedPixels.length })); + showAttackNotification('attack', damagedPixels.length); + + const repairedCount = await repairDamagedPixels(damagedPixels); + + if (repairedCount === damagedPixels.length) { + showAttackNotification('repaired'); + } + } else { + updateStatus(Utils.t('noDamageDetected')); + showPeacefulState(); + } + } catch (error) { + Utils.addDebugLog(`Repair error: ${error.message}`, 'error'); + Utils.showAlert('Repair failed', 'error'); + } finally { + repairRunning = false; + state.running = false; + state.stopFlag = false; + repairBtn.textContent = '🔧 ' + Utils.t('repairPixels'); + repairBtn.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'; + } + }); + + // Auto repair toggle + document.getElementById('autoRepairEnabled').addEventListener('change', (e) => { + state.autoRepairEnabled = e.target.checked; + + if (state.autoRepairEnabled) { + if (!state.imageLoaded) { + Utils.showAlert('Please load a progress file first', 'warning'); + e.target.checked = false; + state.autoRepairEnabled = false; + return; + } + startAutoRepair(); + } else { + stopAutoRepair(); + } + }); + + // Repair interval change + document.getElementById('repairInterval').addEventListener('change', (e) => { + const value = parseInt(e.target.value) || 30; + state.autoRepairInterval = Math.max(10, Math.min(3600, value)); + e.target.value = state.autoRepairInterval; + + Utils.addDebugLog(`Repair interval changed to ${state.autoRepairInterval} seconds`, 'info'); + + if (state.autoRepairEnabled && state.imageLoaded) { + Utils.addDebugLog('Restarting auto repair with new interval', 'info'); + startAutoRepair(); + } + }); + + // Auto token refresh toggle + document.getElementById('autoTokenRefresh').addEventListener('change', (e) => { + state.autoTokenRefresh = e.target.checked; + Utils.addDebugLog(`Auto token refresh ${state.autoTokenRefresh ? 'enabled' : 'disabled'}`, 'info'); + updateSystemStatus(); + + if (state.autoTokenRefresh && state.autonomousMode && !isTokenValid()) { + ensureToken(); + } + }); + + // Token source selection + document.getElementById('tokenSourceSelect').addEventListener('change', (e) => { + state.tokenSource = e.target.value; + Utils.addDebugLog(`Token source changed to: ${state.tokenSource}`, 'info'); + updateSystemStatus(); + + // Invalidate current token to force new generation with new method + if (isTokenValid()) { + Utils.addDebugLog('Invalidating current token due to source change', 'info'); + invalidateToken(); + } + }); + + // Clear debug + document.getElementById('clearDebugBtn').addEventListener('click', () => { + state.debugLogs = []; + updateDebugConsole(); + Utils.addDebugLog('Debug console cleared', 'info'); + Utils.addDebugLog('Enhanced WPlace Autonomous Repair Tool ready', 'info'); + }); + } + + // Enhanced data migration helper + function migrateProgressData(data) { + if (!data.version || data.version < '2.0') { + Utils.addDebugLog('Migrating old progress data format...', 'info'); + + if (!data.state) data.state = {}; + if (!data.imageData) data.imageData = {}; + + data.state.paintWhitePixels = data.state.paintWhitePixels ?? true; + data.state.paintTransparentPixels = data.state.paintTransparentPixels ?? false; + + data.version = '2.0'; + } + + // Additional migrations for autonomous features + if (data.version === '2.0') { + data.state.autonomousMode = data.state.autonomousMode ?? CONFIG.AUTONOMOUS_MODE; + data.state.autoTokenRefresh = data.state.autoTokenRefresh ?? CONFIG.AUTO_TOKEN_REFRESH; + data.state.tokenSource = data.state.tokenSource ?? CONFIG.TOKEN_SOURCE; + data.version = '3.0'; + Utils.addDebugLog('Migrated to autonomous features v3.0', 'info'); + } + + return data; + } + + // Main initialization function + async function initialize() { + Utils.addDebugLog('Starting WPlace Autonomous Repair Tool v3.0...', 'info'); + + // Create UI first + createUI(); + + Utils.addDebugLog('NEW Enhanced Features Initialized:', 'info'); + Utils.addDebugLog('• NEW Turnstile implementation with widget reuse', 'info'); + Utils.addDebugLog('• Enhanced sitekey detection from multiple sources', 'info'); + Utils.addDebugLog('• Sophisticated token management with preloading', 'info'); + Utils.addDebugLog('• Autonomous operation with smart retry logic', 'info'); + Utils.addDebugLog('• Enhanced tile interception and caching', 'info'); + Utils.addDebugLog('• Adaptive repair algorithms with failure handling', 'info'); + Utils.addDebugLog('• Visual notifications and audio alerts', 'info'); + Utils.addDebugLog('• Draggable window with minimize/close controls', 'info'); + + if (state.autonomousMode) { + Utils.addDebugLog('AUTONOMOUS MODE ACTIVE - Tool will operate independently', 'success'); + Utils.addDebugLog('Instructions for autonomous operation:', 'info'); + Utils.addDebugLog('1. Wait for initial setup to complete', 'info'); + Utils.addDebugLog('2. Load a progress file using "Load Progress File"', 'info'); + Utils.addDebugLog('3. Enable "Auto Repair" to start autonomous monitoring', 'info'); + Utils.addDebugLog('4. Tool will automatically scan and repair damage', 'info'); + Utils.addDebugLog('5. Tokens will be generated and refreshed automatically', 'info'); + Utils.addDebugLog('6. Monitor debug console for autonomous operation logs', 'info'); + } else { + Utils.addDebugLog('Manual mode active - use controls for manual operation', 'info'); + } + + // Show autonomous mode status + updateStatus(state.autonomousMode ? Utils.t('autonomousModeActive') : 'Manual mode ready - Load file and enable auto repair to start'); + + Utils.addDebugLog('WPlace Autonomous Repair Tool ready for operation', 'success'); + } + + // Enhanced cleanup function + function cleanup() { + Utils.addDebugLog('Cleaning up autonomous systems...', 'info'); + + // Clear all timers + if (state.autoRepairTimer) { + clearInterval(state.autoRepairTimer); + state.autoRepairTimer = null; + } + + if (state.tokenRetryTimer) { + clearTimeout(state.tokenRetryTimer); + state.tokenRetryTimer = null; + } + + if (state.tokenPreloadTimer) { + clearTimeout(state.tokenPreloadTimer); + state.tokenPreloadTimer = null; + } + + // Cleanup Turnstile + Utils.cleanupTurnstile(); + + // Set stop flag + state.stopFlag = true; + + Utils.addDebugLog('Cleanup completed', 'info'); + } + + // Add cleanup on page unload + window.addEventListener('beforeunload', cleanup); + + // Handle page visibility changes for autonomous mode + document.addEventListener('visibilitychange', () => { + if (state.autonomousMode) { + if (document.hidden) { + Utils.addDebugLog('Page hidden - autonomous systems continue running', 'info'); + } else { + Utils.addDebugLog('Page visible - autonomous systems active', 'info'); + // Refresh status when page becomes visible + updateCharges(); + updateTokenStatus(); + } + } + }); + + // Enhanced error handling for autonomous mode + window.addEventListener('error', (event) => { + if (state.autonomousMode) { + Utils.addDebugLog(`Global error in autonomous mode: ${event.error?.message || 'Unknown error'}`, 'error'); + } + }); + + window.addEventListener('unhandledrejection', (event) => { + if (state.autonomousMode) { + Utils.addDebugLog(`Unhandled promise rejection in autonomous mode: ${event.reason}`, 'error'); + } + }); + + // Start the enhanced autonomous application + initialize().catch(error => { + console.error('Failed to initialize autonomous repair tool:', error); + Utils.addDebugLog(`Initialization failed: ${error.message}`, 'error'); + }); + +})(); diff --git a/Extension/themes/acrylic.css b/Extension/themes/acrylic.css new file mode 100644 index 0000000..f94db2a --- /dev/null +++ b/Extension/themes/acrylic.css @@ -0,0 +1,603 @@ +/* WPlace Auto-Image Bot - Acrylic Theme */ +/* Beautiful theme with translucent blurred panels */ + +@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); + +@font-face { + font-family: 'Apple Color Emoji'; + src: url('https://raw.githubusercontent.com/GORAlexComp/AppleColorEmojiFont/refs/heads/main/fonts/AppleColorEmoji.ttf'); +} + +/* Main Container */ +.wplace-theme-acrylic #wplace-image-bot-container { + background: var(--wplace-primary); + color: var(--wplace-text); + border-radius: var(--wplace-radius); + box-shadow: none; + backdrop-filter: var(--wplace-backdrop); + border: var(--wplace-border-width) var(--wplace-border-style) + var(--wplace-border-color); + min-width: 300px; +} + +.wplace-theme-acrylic #wplace-image-bot-container * { + font-size: 15px; + line-height: normal; +} + +.wplace-theme-acrylic .wplace-content { + display: flex; + flex-direction: column; + gap: 10px; + padding: 10px; +} + +#wplace-image-bot-container.wplace-dragging { + transition: none !important; + box-shadow: none !important; + transform: none !important; +} + +/* Stats */ +.wplace-theme-acrylic #wplace-stats-container { + background: var(--wplace-primary); + color: var(--wplace-text); + border-radius: var(--wplace-radius); + box-shadow: none; + backdrop-filter: var(--wplace-backdrop); + border: var(--wplace-border-width) var(--wplace-border-style) + var(--wplace-border-color); +} + +.wplace-theme-acrylic .wplace-stats { + margin-bottom: 0; +} + +.wplace-theme-acrylic .wplace-colors-section { + border-top: none; + margin: 0; +} + +/* Settings Container */ +.wplace-theme-acrylic #wplace-settings-container { + background: var(--wplace-primary) !important; + color: var(--wplace-text) !important; + border-radius: var(--wplace-radius) !important; + box-shadow: none !important; + backdrop-filter: var(--wplace-backdrop) !important; + border: var(--wplace-border-width) var(--wplace-border-style) + var(--wplace-border-color) !important; + max-width: 400px !important; +} + +.wplace-theme-acrylic .wplace-settings-header { + background: transparent; + padding: 0; +} + +.wplace-theme-acrylic .wplace-settings-header:hover { + background: transparent !important; +} + +.wplace-theme-acrylic .wplace-settings-icon { + animation: none; + font-size: 15px; +} + +.wplace-theme-acrylic .wplace-settings-content { + padding: 10px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.wplace-theme-acrylic .wplace-settings-footer { + border-top: none; + padding: 10px; +} + +.wplace-theme-acrylic .wplace-settings-section { + margin: 0; +} + +.wplace-theme-acrylic .wplace-settings-section-wrapper, +.wplace-theme-acrylic .wplace-overlay-wrapper, +.wplace-theme-acrylic .wplace-batch-controls { + padding: 0 !important; + background: transparent !important; + border: none !important; +} + +/* Headers */ +.wplace-theme-acrylic .wplace-header, +.wplace-theme-acrylic .wplace-settings-title-wrapper { + height: 40px; + padding: 7px 7px 7px 10px; + font-weight: 600; + background: var(--wplace-accent); + color: var(--wplace-highlight); +} + +.wplace-theme-acrylic .wplace-header span, +.wplace-theme-acrylic .wplace-settings-title { + max-width: 150px; + font-size: 15px; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.wplace-theme-acrylic .wplace-header-controls #compactBtn { + display: none; +} + +.wplace-theme-acrylic .wplace-header-btn, +.wplace-theme-acrylic .wplace-settings-close-btn { + background: #2e2e2e; + color: var(--wplace-highlight); + border: none; + font-size: 12px; + border-radius: 4px; + width: 24px; + height: 24px; +} + +.wplace-theme-acrylic .wplace-header-btn i { + font-size: 12px !important; +} + +.wplace-theme-acrylic .wplace-header-btn:hover, +.wplace-theme-acrylic .wplace-settings-close-btn:hover { + background: transparent; + transform: none; +} + +/* Sections */ +.wplace-theme-acrylic .wplace-section { + display: flex; + flex-direction: column; + gap: 8px; + background: transparent; + padding: 0; + border: none; + margin: 0; +} + +.wplace-theme-acrylic .wplace-section-title { + color: var(--wplace-highlight); + text-transform: capitalize; + font-size: 13px; + font-weight: 600; + pointer-events: none; + letter-spacing: unset; +} + +.wplace-theme-acrylic .wplace-section-title i { + display: none; +} + +.wplace-theme-acrylic .wplace-status-section { + background: transparent; + padding: 0; + margin: 0; + border: none; + display: flex; + flex-direction: column; + gap: 8px; +} + +/* Status Styling */ +.wplace-theme-acrylic .wplace-stats { + background: transparent; + padding: 0; + border: none; +} + +.wplace-theme-acrylic .wplace-stat-value { + color: var(--wplace-highlight); +} + +.wplace-theme-acrylic .wplace-status { + border-radius: var(--wplace-radius); + border: none; + box-shadow: none; + padding: 10px; + font-weight: 500; +} + +.wplace-theme-acrylic .status-default { + background: color-mix(in srgb, white 15%, transparent) !important; +} + +.wplace-theme-acrylic .status-success { + background: color-mix(in srgb, var(--wplace-success) 15%, transparent); + color: var(--wplace-success); +} + +.wplace-theme-acrylic .status-error { + background: color-mix(in srgb, var(--wplace-error) 15%, transparent); + color: var(--wplace-error); +} + +.wplace-theme-acrylic .status-warning { + background: color-mix(in srgb, var(--wplace-warning) 15%, transparent); + color: var(--wplace-warning); +} + +/* Buttons */ +.wplace-theme-acrylic .wplace-btn { + border-radius: 8px; + border: none; + height: max-content; + padding: 10px; + gap: 5px; + flex-direction: column; + align-items: flex-start; + line-height: normal; + text-align: left; + font-size: 15px !important; + box-shadow: none; +} + +.wplace-theme-acrylic .wplace-btn::before { + display: none; +} + +.wplace-theme-acrylic .wplace-btn:disabled { + box-shadow: none !important; +} + +.wplace-theme-acrylic .wplace-btn:hover:not(:disabled) { + transform: none; + animation: none; + box-shadow: none !important; + filter: brightness(0.9); +} + +.wplace-theme-acrylic .wplace-btn-primary, +.wplace-theme-acrylic .wplace-btn-upload { + background: white !important; + color: black !important; + border: none !important; +} + +.wplace-theme-acrylic .wplace-btn-upload:hover:not(:disabled) { + background: revert !important; +} + +.wplace-theme-acrylic .wplace-btn-secondary, +.wplace-theme-acrylic .wplace-btn-select, +.wplace-theme-acrylic .wplace-btn-file, +.wplace-theme-acrylic .wplace-btn-overlay { + background: var(--wplace-secondary); +} + +.wplace-theme-acrylic .wplace-btn-start { + background: var(--wplace-success); + color: white; +} + +.wplace-theme-acrylic .wplace-btn-stop { + background: var(--wplace-error); + color: white; +} + +.wplace-theme-acrylic .wplace-btn.active { + background: white; + color: black; + box-shadow: none !important; +} + +.wplace-theme-acrylic .wplace-btn.active i { + filter: none; +} + +.wplace-theme-acrylic .wplace-settings-apply-btn { + background: white; + color: black; + border-radius: 8px; +} + +/* Compact Input Buttons */ +.wplace-theme-acrylic .wplace-input-btn-small, +.wplace-theme-acrylic .wplace-input-btn-small:hover, +.wplace-theme-acrylic .wplace-input-btn-small:active { + background: white; + color: black; + border: none; + transform: none; + border-radius: 4px; + box-shadow: none; + font-weight: 500; + width: 25px; + height: 25px; +} + +/* Alerts */ +.wplace-theme-acrylic .wplace-alert-base { + border-radius: var(--wplace-radius); + backdrop-filter: var(--wplace-backdrop); + border: none; + box-shadow: none; +} + +.wplace-theme-acrylic .wplace-alert-info { + background: color-mix(in srgb, #0000e5 50%, transparent); +} + +.wplace-theme-acrylic .wplace-alert-success { + background: color-mix(in srgb, var(--wplace-success) 50%, transparent); +} + +.wplace-theme-acrylic .wplace-alert-error { + background: color-mix(in srgb, var(--wplace-error) 50%, transparent); +} + +.wplace-theme-acrylic .wplace-alert-warning { + background: color-mix(in srgb, var(--wplace-warning) 50%, transparent); +} + +/* Progress Bar */ +.wplace-theme-acrylic .wplace-progress { + background: var(--wplace-secondary); + border-radius: 4px; + margin: 0; + border: none; +} + +.wplace-theme-acrylic .wplace-progress-bar { + background: white; +} + +.wplace-theme-acrylic .wplace-progress-bar::after { + display: none; +} + +/* Form Controls */ +.wplace-theme-acrylic .wplace-settings-checkbox, +.wplace-theme-acrylic .wplace-notification-checkbox, +.wplace-theme-acrylic .wplace-speed-checkbox { + cursor: pointer; + width: 20px; + height: 20px; + flex-shrink: 0; + accent-color: var(--wplace-highlight); +} + +.wplace-theme-acrylic .wplace-settings-select, +.wplace-theme-acrylic .wplace-settings-number-input, +.wplace-theme-acrylic .wplace-cooldown-input, +.wplace-theme-acrylic .wplace-notification-interval-input { + background: rgb(255 255 255 / 10%); + color: white; + border: 1px solid rgb(255 255 255 / 25%); + border-radius: 8px; + box-shadow: none; + transition-duration: 0.3s; +} + +.wplace-theme-acrylic .wplace-settings-select:hover, +.wplace-theme-acrylic .wplace-settings-number-input:hover, +.wplace-theme-acrylic .wplace-cooldown-input:hover, +.wplace-theme-acrylic .wplace-notification-interval-input:hover { + border-color: rgb(255 255 255 / 50%); +} + +.wplace-theme-acrylic .wplace-settings-select:focus, +.wplace-theme-acrylic .wplace-settings-number-input:focus, +.wplace-theme-acrylic .wplace-cooldown-input:focus, +.wplace-theme-acrylic .wplace-notification-interval-input:focus { + border-color: white; +} + +.wplace-theme-acrylic .wplace-settings-option { + background: #2d3748; + color: white; +} + +.wplace-theme-acrylic .wplace-settings-select option { + background: #2d3748; + color: white; +} + +.wplace-theme-acrylic .wplace-cooldown-input { + border-radius: 4px !important; +} + +.wplace-theme-acrylic .wplace-cooldown-input:focus { + background: rgb(255 255 255 / 10%); + box-shadow: none; +} + +.wplace-theme-acrylic .wplace-overlay-opacity-value { + background-color: var(--wplace-secondary) !important; + border: none !important; +} + +/* Sliders */ +.wplace-theme-acrylic .wplace-slider, +.wplace-theme-acrylic .wplace-speed-slider, +.wplace-theme-acrylic .wplace-overlay-opacity-slider { + background: var(--wplace-secondary) !important; + border-radius: 3px !important; + height: 6px !important; +} + +.wplace-theme-acrylic .wplace-slider::-webkit-slider-thumb, +.wplace-theme-acrylic .resize-slider::-webkit-slider-thumb, +.wplace-theme-acrylic .wplace-speed-slider::-webkit-slider-thumb, +.wplace-theme-acrylic .wplace-overlay-opacity-slider::-webkit-slider-thumb { + width: 16px !important; + height: 16px !important; + border-radius: 4px !important; + background: white !important; + box-shadow: none !important; + transform: none !important; +} + +.wplace-theme-acrylic .wplace-speed-value { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + border-radius: 8px; + color: white; + box-shadow: 0 3px 10px rgb(79 172 254 / 30%); + border: 1px solid rgb(255 255 255 / 20%); +} + +.wplace-theme-acrylic .resize-slider { + -webkit-appearance: revert; +} + +/* Layout Controls */ +.wplace-theme-acrylic .wplace-slider-container { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + margin: 0; +} + +.wplace-theme-acrylic .wplace-input-label-compact { + color: rgba(255, 255, 255, 0.75); + margin-left: 4px; + text-transform: lowercase; + white-space: nowrap; +} + +.wplace-theme-acrylic .wplace-cooldown-control { + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.wplace-theme-acrylic .wplace-cooldown-control label { + font-weight: 400; + margin-bottom: 0; + display: block; +} + +.wplace-theme-acrylic .wplace-cooldown-input-group { + margin: 0; + background: transparent; +} + +/* Color Elements */ +.wplace-theme-acrylic .wplace-color-item-name { + color: #ccc; +} + +.wplace-theme-acrylic .wplace-color-swatch { + border: 1px solid rgb(255 255 255 / 20%); + border-radius: 4px; +} + +.wplace-theme-acrylic .wplace-color-swatch:not(.active) { + filter: grayscale(80%); +} + +.wplace-theme-acrylic .wplace-color-swatch.unavailable { + border-color: #666; +} + +.wplace-theme-acrylic .wplace-color-swatch.unavailable:not(.active) { + filter: grayscale(90%); +} + +.wplace-theme-acrylic .wplace-color-swatch.active::after { + text-shadow: 0 0 3px black; +} + +.wplace-theme-acrylic .wplace-stat-color-swatch { + border-radius: 3px; + border: 1px solid rgb(255 255 255 / 10%); + box-shadow: inset 0 0 2px rgb(0 0 0 / 50%); +} + +.wplace-theme-acrylic .wplace-stat-colors-grid { + background: var(--wplace-secondary); + border-radius: 8px; +} + +.wplace-theme-acrylic .wplace-color-divider { + background: rgb(255 255 255 / 10%); +} + +/* Icons */ +.wplace-theme-acrylic .wplace-icon-key, +.wplace-theme-acrylic .wplace-icon-robot, +.wplace-theme-acrylic .wplace-icon-speed, +.wplace-theme-acrylic .wplace-icon-bell, +.wplace-theme-acrylic .wplace-icon-palette, +.wplace-theme-acrylic .wplace-icon-globe, +.wplace-theme-acrylic .wplace-icon-paint, +.wplace-theme-acrylic .wplace-icon-eye, +.wplace-theme-acrylic .wplace-icon-compass, +.wplace-theme-acrylic .wplace-icon-route, +.wplace-theme-acrylic .wplace-icon-table, +.wplace-theme-acrylic .wplace-icon-dice { + color: white; +} + +/* Typography */ +.wplace-theme-acrylic .wplace-cooldown-unit { + display: none; +} + +.wplace-theme-acrylic .wplace-settings-toggle-title, +.wplace-theme-acrylic .wplace-overlay-opacity-label { + font-size: 15px !important; +} + +.wplace-theme-acrylic .wplace-settings-description, +.wplace-theme-acrylic .wplace-random-batch-description, +.wplace-theme-acrylic .wplace-settings-toggle-description { + font-size: 12px; + color: rgb(255 255 255 / 75%) !important; +} + +/* Turnstile Overlay */ +.wplace-theme-acrylic .wplace-turnstile-overlay { + background: var(--wplace-primary); + border-radius: var(--wplace-radius); + box-shadow: none; + backdrop-filter: var(--wplace-backdrop); + border: none; + padding: 16px !important; + color: var(--wplace-text) !important; +} + +.wplace-theme-acrylic .wplace-turnstile-hide-btn { + border-radius: 8px !important; + backdrop-filter: var(--wplace-backdrop) !important; + background: var(--wplace-primary) !important; + top: -25px !important; + right: 0 !important; + transition: all 0.3s ease !important; +} + +.wplace-theme-acrylic .wplace-turnstile-hide-btn:hover { + background: white !important; + color: black !important; +} + +/* Resize Tools */ +.wplace-theme-acrylic .resize-tools button { + border-radius: 6px; + border: 1px solid rgb(255 255 255 / 20%); + background: rgb(255 255 255 / 6%); + color: #fff; +} + +.wplace-theme-acrylic .resize-overlay { + background: rgb(0 0 0 / 50%); +} + +/* TODO: More resize menu styles */ +.wplace-theme-acrylic .resize-container { + background: var(--wplace-secondary); + backdrop-filter: var(--wplace-backdrop); + border: none; + border-radius: var(--wplace-radius); + color: var(--wplace-text); +} diff --git a/Extension/themes/classic-light.css b/Extension/themes/classic-light.css new file mode 100644 index 0000000..424ad36 --- /dev/null +++ b/Extension/themes/classic-light.css @@ -0,0 +1,598 @@ +/* WPlace Auto-Image Bot - Classic Light Theme */ + +/* Clean, bright theme based on classic design with light backgrounds */ + +.wplace-theme-classic-light { + /* === CORE COLORS === */ + --wplace-primary: #fff; /* Clean white */ + --wplace-secondary: #f8f9fa; /* Light gray */ + --wplace-accent: #e9ecef; /* Lighter gray */ + --wplace-text: #212529; /* Dark text */ + --wplace-highlight: #6f42c1; /* Purple highlight */ + --wplace-success: #28a745; /* Bootstrap green */ + --wplace-error: #dc3545; /* Bootstrap red */ + --wplace-warning: #ffc107; /* Bootstrap yellow */ + + /* === UI PROPERTIES === */ + --wplace-radius: 8px; + --wplace-btn-radius: 12px; + --wplace-border-style: solid; + --wplace-border-width: 1px; + --wplace-border-color: #415a77; + --wplace-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%); + --wplace-backdrop: blur(10px); + --wplace-font: 'Segoe UI', roboto, sans-serif; + + /* === EXTENDED COLOR PALETTE === */ + + /* Icon colors */ + --wplace-icon-primary: #4facfe; /* Light blue */ + --wplace-icon-secondary: #00f2fe; /* Cyan */ + --wplace-icon-palette: #f093fb; /* Pink */ + + /* Additional UI colors */ + --wplace-danger: #dc3545; /* Bootstrap red */ + --wplace-danger-dark: #c82333; /* Darker red */ + --wplace-muted-text: rgb(33 37 41 / 70%); + --wplace-highlight-secondary: #d3a4ff; + + /* Text variants */ + --wplace-text-secondary: rgb(33 37 41 / 90%); + --wplace-text-muted: rgb(33 37 41 / 70%); + --wplace-text-dim: rgb(33 37 41 / 60%); + --wplace-text-faded: rgb(33 37 41 / 80%); + + /* Background variants */ + --wplace-bg-input: #f3f3f3; /* neutral-100 */ + --wplace-bg-subtle: #f3f3f3; /* neutral-100 — hover */ + --wplace-bg-faint: #e0e0e0; /* neutral-200 */ + --wplace-bg-ghost: #d1d1d1; /* neutral-300 */ + --wplace-bg-whisper: #e0e0e0; /* can be used as whisper on hover */ + + /* Border variants */ + --wplace-border-subtle: #d1d1d1; /* neutral-300 */ + --wplace-border-faint: #e0e0e0; /* neutral-200 */ + --wplace-border-ghost: #e0e0e0; /* neutral-200 */ + --wplace-border-ultra-faint: #e0e0e0; /* neutral-200 — very faint */ + + /* Shadow variants */ + --wplace-shadow-drag: 0 12px 40px rgb(0 0 0 / 20%), 0 0 0 2px rgb(111 66 193 / 30%); + --wplace-shadow-notification: 0 4px 12px rgb(0 0 0 / 15%); + --wplace-shadow-slider-thumb: 0 3px 6px rgb(0 0 0 / 20%), 0 0 0 2px var(--wplace-icon-primary); + --wplace-shadow-slider-hover: 0 4px 8px rgb(0 0 0 / 25%), 0 0 0 3px var(--wplace-icon-primary); + + /* Animation colors */ + --wplace-pulse-start: rgb(40 167 69 / 70%); + --wplace-pulse-mid: rgb(40 167 69 / 0%); + --wplace-pulse-end: rgb(40 167 69 / 0%); + + /* Slider colors */ + --wplace-slider-thumb-bg: #6f42c1; + --wplace-slider-track-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); +} + +/* === COMPONENT STYLING === */ + +/* Main container with clean light styling */ +.wplace-theme-classic-light #wplace-image-bot-container { + background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; + color: var(--wplace-text) !important; + border: 1px solid rgb(0 0 0 / 15%) !important; + border-radius: 12px !important; + box-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + backdrop-filter: blur(10px) !important; +} + + +/* Stats container with proper contrast */ +.wplace-theme-classic-light #wplace-stats-container { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 12px !important; + box-shadow: 0 8px 32px rgb(0 0 0 / 15%) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + color: var(--wplace-text) !important; + position: fixed !important; + overflow: hidden !important; + z-index: 9998 !important; +} + +/* Headers with light gradient */ +.wplace-theme-classic-light .wplace-header { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; + color: var(--wplace-highlight) !important; + border-bottom: 1px solid rgb(0 0 0 / 15%) !important; + text-shadow: none !important; +} + +/* Stats header styling */ +.wplace-theme-classic-light #wplace-stats-container .wplace-header { + background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; + color: var(--wplace-text) !important; + border-bottom: 1px solid rgb(0 0 0 / 20%) !important; + text-shadow: none !important; + font-weight: 600 !important; +} + +/* Stats title specific styling */ +.wplace-theme-classic-light #wplace-stats-container .wplace-header .wplace-stats-title { + color: var(--wplace-text) !important; + text-shadow: none !important; +} + +/* Comprehensive text and element styling for light theme - scoped to bot containers only */ + +.wplace-theme-classic-light .wplace-status, +.wplace-theme-classic-light .wplace-stats, +.wplace-theme-classic-light .wplace-section, +.wplace-theme-classic-light .wplace-controls, +.wplace-theme-classic-light .wplace-data-management, +.wplace-theme-classic-light .wplace-cooldown-settings { + color: var(--wplace-text) !important; +} + +/* Buttons with light styling */ +.wplace-theme-classic-light .wplace-btn { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 12px !important; + color: var(--wplace-text) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + font-weight: 500 !important; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; +} + +/* Data management section buttons */ +.wplace-theme-classic-light .wplace-data-management .wplace-btn { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + color: #212529 !important; + font-weight: 500 !important; +} + +.wplace-theme-classic-light .wplace-btn:hover:not(:disabled) { + background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; + box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important; + transform: translateY(-1px) !important; +} + +.wplace-theme-classic-light .wplace-data-management .wplace-btn:hover:not(:disabled) { + background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; + box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important; +} + +/* Settings dialog */ +.wplace-theme-classic-light #wplace-settings-container { + background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; + border: 1px solid rgb(0 0 0 / 15%) !important; + border-radius: 12px !important; + box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + backdrop-filter: blur(10px) !important; + color: var(--wplace-text) !important; +} + +/* Settings dialog text elements */ +.wplace-theme-classic-light .wplace-stat-colors-grid * { + color: var(--wplace-text) !important; + font-weight: 500 !important; +} + +.wplace-theme-classic-light #wplace-settings-container * { + color: var(--wplace-text) !important; +} + +.wplace-theme-classic-light #wplace-settings-container .wplace-settings-section-wrapper * { + color: var(--wplace-text) !important; +} + +/* Color palette text labels */ +.wplace-theme-classic-light .wplace-color-label { + color: var(--wplace-text) !important; + font-weight: 500 !important; + text-shadow: none !important; +} + +.wplace-theme-classic-light .wplace-color-name { + color: var(--wplace-text) !important; + font-weight: 500 !important; +} + +.wplace-theme-classic-light .wplace-color-item-name { + color: #000 !important; + font-weight: 600 !important; + text-shadow: none !important; +} + +/* Resize dialog */ +.wplace-theme-classic-light .resize-container { + background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; + border: 1px solid rgb(0 0 0 / 15%) !important; + border-radius: 12px !important; + box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + backdrop-filter: blur(10px) !important; +} + +/* Settings header */ +.wplace-theme-classic-light .wplace-settings-header { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; + border-bottom: 1px solid rgb(0 0 0 / 15%) !important; +} + +.wplace-theme-classic-light .wplace-settings-title { + color: var(--wplace-text) !important; + text-shadow: none !important; +} + +.wplace-theme-classic-light .wplace-settings-close-btn { + background: rgb(0 0 0 / 5%) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 50% !important; + color: var(--wplace-text) !important; + transition: all 0.3s ease !important; +} + +.wplace-theme-classic-light .wplace-settings-close-btn:hover { + background: rgb(220 53 69 / 10%) !important; + border-color: var(--wplace-error) !important; + box-shadow: 0 0 12px rgb(220 53 69 / 30%) !important; +} + +/* Section titles */ +.wplace-theme-classic-light .wplace-section-title { + color: var(--wplace-highlight) !important; + text-shadow: none !important; + font-weight: 600 !important; +} + +/* Button variants with classic light colors */ +.wplace-theme-classic-light .wplace-btn-start { + background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important; + color: white !important; + font-weight: 600 !important; +} + +.wplace-theme-classic-light .wplace-btn-start:hover:not(:disabled) { + box-shadow: 0 4px 12px rgb(40 167 69 / 40%) !important; +} + +.wplace-theme-classic-light .wplace-btn-stop { + background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important; + color: white !important; + font-weight: 600 !important; +} + +.wplace-theme-classic-light .wplace-btn-stop:hover:not(:disabled) { + box-shadow: 0 4px 12px rgb(220 53 69 / 40%) !important; +} + +.wplace-theme-classic-light .wplace-btn-upload { + background: rgb(111 66 193 / 10%) !important; + border: 2px dashed var(--wplace-highlight) !important; + color: var(--wplace-highlight) !important; +} + +.wplace-theme-classic-light .wplace-btn-upload:hover:not(:disabled) { + background: rgb(111 66 193 / 15%) !important; + box-shadow: 0 4px 12px rgb(111 66 193 / 20%) !important; +} + +/* Progress bars with clean light styling */ +.wplace-theme-classic-light .wplace-progress { + background: rgb(0 0 0 / 10%) !important; + border: 1px solid rgb(0 0 0 / 15%) !important; + border-radius: 12px !important; +} + +.wplace-theme-classic-light .wplace-progress-bar { + background: linear-gradient(135deg, #6f42c1 0%, #9370db 100%) !important; + box-shadow: none !important; +} + +.wplace-theme-classic-light .wplace-progress-bar::after { + background: linear-gradient(90deg, transparent, rgb(255 255 255 / 30%), transparent) !important; +} + +/* Status indicators with light colors */ +.wplace-theme-classic-light .status-success { + background: rgb(40 167 69 / 10%) !important; + border-color: var(--wplace-success) !important; + color: var(--wplace-success) !important; + box-shadow: 0 0 15px rgb(40 167 69 / 20%) !important; + text-shadow: none !important; +} + +.wplace-theme-classic-light .status-error { + background: rgb(220 53 69 / 10%) !important; + border-color: var(--wplace-error) !important; + color: var(--wplace-error) !important; + box-shadow: 0 0 15px rgb(220 53 69 / 20%) !important; + text-shadow: none !important; +} + +.wplace-theme-classic-light .status-default { + background: rgb(111 66 193 / 10%) !important; + border-color: var(--wplace-highlight) !important; + color: var(--wplace-highlight) !important; + text-shadow: none !important; +} + +.wplace-theme-classic-light .wplace-stat-label { + color: var(--wplace-text) !important; + text-shadow: none !important; + font-weight: 500 !important; +} + +.wplace-theme-classic-light .wplace-stat-value { + color: var(--wplace-highlight) !important; + text-shadow: none !important; + font-weight: 600 !important; +} + +/* Sections with light styling */ +.wplace-theme-classic-light .wplace-section { + background: rgb(0 0 0 / 3%) !important; + border: 1px solid rgb(0 0 0 / 10%) !important; + border-radius: 12px !important; +} + +.wplace-theme-classic-light .wplace-status-section { + background: rgb(0 0 0 / 3%) !important; + border: 1px solid rgb(0 0 0 / 10%) !important; + border-radius: 12px !important; +} + +.wplace-theme-classic-light .wplace-settings-section-wrapper { + background: rgb(0 0 0 / 3%) !important; + border: 1px solid rgb(0 0 0 / 10%) !important; + border-radius: 12px !important; +} + +/* Form controls with light styling */ +.wplace-theme-classic-light .wplace-settings-select { + background: #fff !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 8px !important; + color: var(--wplace-text) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important; +} + +.wplace-theme-classic-light .wplace-settings-select:focus { + border-color: var(--wplace-highlight) !important; + box-shadow: 0 0 0 2px rgb(111 66 193 / 30%) !important; +} + +/* Dropdown menu options */ +.wplace-theme-classic-light .wplace-settings-select option { + background: #fff !important; + color: var(--wplace-text) !important; +} + +.wplace-theme-classic-light .wplace-settings-option { + background: #fff !important; + color: var(--wplace-text) !important; +} + +/* Sliders with classic gradient */ +.wplace-theme-classic-light .wplace-speed-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; + border-radius: 4px !important; +} + +.wplace-theme-classic-light .wplace-overlay-opacity-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; + border-radius: 4px !important; +} + +.wplace-theme-classic-light .wplace-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%) !important; + border-radius: 4px !important; +} + +.wplace-theme-classic-light .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--wplace-slider-thumb-bg); + border: 1px solid rgb(0 0 0 / 20%); + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 2px 4px rgb(0 0 0 / 20%); +} + +.wplace-theme-classic-light .wplace-speed-value { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important; + border-radius: 8px !important; + color: white !important; + font-weight: 600 !important; + box-shadow: 0 3px 10px rgb(79 172 254 / 30%) !important; + border: 1px solid rgb(0 0 0 / 10%) !important; +} + +/* Settings labels */ +.wplace-theme-classic-light .wplace-settings-section-label { + color: var(--wplace-highlight) !important; + text-shadow: none !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + font-weight: 600 !important; +} + +/* Icon colors for classic light theme */ +.wplace-theme-classic-light .wplace-icon-key { color: #4facfe; } +.wplace-theme-classic-light .wplace-icon-robot { color: #4facfe; } +.wplace-theme-classic-light .wplace-icon-speed { color: #4facfe; } +.wplace-theme-classic-light .wplace-icon-bell { color: #ffc107; } +.wplace-theme-classic-light .wplace-icon-palette { color: #f093fb; } +.wplace-theme-classic-light .wplace-icon-globe { color: #ffeaa7; } +.wplace-theme-classic-light .wplace-icon-paint { color: #4facfe; } +.wplace-theme-classic-light .wplace-icon-eye { color: #6f42c1; } + +/* Clean light theme animations */ +@keyframes light-shimmer { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(200%); + } +} + +/* Turnstile/CF checkbox overlay - CRITICAL FIX */ +.wplace-theme-classic-light .wplace-turnstile-overlay { + background: rgb(255 255 255 / 98%) !important; + border-radius: 12px !important; + box-shadow: 0 8px 32px rgb(0 0 0 / 30%) !important; + backdrop-filter: blur(10px) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + color: var(--wplace-text) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; +} + +.wplace-theme-classic-light .wplace-turnstile-title { + color: var(--wplace-text) !important; +} + +.wplace-theme-classic-light .wplace-turnstile-hide-btn { + color: var(--wplace-text) !important; + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 6px !important; + background: rgb(0 0 0 / 5%) !important; +} + +.wplace-theme-classic-light .wplace-turnstile-hide-btn:hover { + background: rgb(0 0 0 / 10%) !important; +} + +/* Dual control layout styling for classic light theme */ +.wplace-theme-classic-light .wplace-dual-control-compact { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + margin: 8px 0; + flex-wrap: wrap; +} + +.wplace-theme-classic-light .wplace-slider-container-compact { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 160px; +} + +.wplace-theme-classic-light .wplace-speed-slider-container-compact { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 160px; +} + +.wplace-theme-classic-light .wplace-input-group-compact { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.wplace-theme-classic-light .wplace-input-btn-compact { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + color: white; + border: none; + border-radius: 4px; + width: 22px; + height: 22px; + cursor: pointer; + font-weight: bold; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + +.wplace-theme-classic-light .wplace-input-btn-compact:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0,0,0,0.3); + background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%); +} + +.wplace-theme-classic-light .wplace-input-btn-compact:active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + +.wplace-theme-classic-light .wplace-number-input-compact { + background: #fff; + border: 1px solid rgb(0 0 0 / 20%); + border-radius: 4px; + color: var(--wplace-text); + padding: 4px 8px; + font-size: 12px; + width: 50px; + text-align: center; + transition: all 0.2s ease; + height: 22px; +} + +.wplace-theme-classic-light .wplace-number-input-compact:focus { + outline: none; + border-color: #4facfe; + box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3); + background: #f8f9fa; +} + +.wplace-theme-classic-light .wplace-input-label-compact { + color: var(--wplace-text); + font-size: 11px; + margin-left: 4px; + white-space: nowrap; +} + +.wplace-theme-classic-light .wplace-batch-size-header { + margin-bottom: 8px; +} + +.wplace-theme-classic-light .wplace-batch-size-label { + color: var(--wplace-highlight); + font-weight: 500; + font-size: 13px; +} + +/* Cooldown control styling for classic light theme */ +.wplace-theme-classic-light .wplace-cooldown-control { + margin: 12px 0; +} + +.wplace-theme-classic-light .wplace-cooldown-control label { + color: var(--wplace-highlight); + font-weight: 500; + font-size: 13px; + margin-bottom: 8px; + display: block; +} + +/* Color swatches with light styling */ +.wplace-theme-classic-light .wplace-color-swatch { + border: 1px solid rgb(0 0 0 / 20%) !important; + border-radius: 4px !important; + box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important; +} + +.wplace-theme-classic-light .wplace-color-swatch:hover { + box-shadow: 0 4px 16px rgb(111 66 193 / 30%) !important; + transform: translateY(-1px); +} + +.wplace-theme-classic-light .wplace-stat-colors-grid { + background: rgb(0 0 0 / 5%) !important; + border-radius: 8px !important; + border: 1px solid rgb(0 0 0 / 10%) !important; +} diff --git a/Extension/themes/classic.css b/Extension/themes/classic.css new file mode 100644 index 0000000..634675e --- /dev/null +++ b/Extension/themes/classic.css @@ -0,0 +1,725 @@ +/* WPlace Auto-Image Bot - Classic Theme */ +/* Clean, modern UI with gradients and subtle effects */ + +.wplace-theme-classic { + --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-success: #0f0; + --wplace-error: #f00; + --wplace-warning: #fa0; + --wplace-radius: 12px; + --wplace-btn-radius: 16px; + --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; + --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; + --wplace-highlight-secondary: #d3a4ff; + + /* Slider colors */ + --wplace-slider-track-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); +} + +/* Classic theme container styling */ +:root #wplace-image-bot-container, +.wplace-theme-classic #wplace-image-bot-container { + 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); +} + +:root #wplace-stats-container, +.wplace-theme-classic #wplace-stats-container { + 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); +} + +:root #wplace-settings-container, +.wplace-theme-classic #wplace-settings-container { + 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); +} + +:root .wplace-header, +.wplace-theme-classic .wplace-header { + background: var(--wplace-secondary); + color: var(--wplace-highlight); + text-shadow: 0 1px 2px rgb(0 0 0 / 50%); +} + +:root .wplace-section-title, +.wplace-theme-classic .wplace-section-title { + color: var(--wplace-highlight); +} + +:root .wplace-stat-value, +.wplace-theme-classic .wplace-stat-value { + color: var(--wplace-highlight); +} + +:root .wplace-status-section, +.wplace-theme-classic .wplace-status-section { + border-radius: var(--wplace-radius); + background: rgb(255 255 255 / 3%); + border: 1px solid rgb(255 255 255 / 10%); +} + +:root .wplace-section, +.wplace-theme-classic .wplace-section { + border-radius: var(--wplace-radius); + background: rgb(255 255 255 / 3%); +} + +:root .wplace-stats, +.wplace-theme-classic .wplace-stats { + border-radius: var(--wplace-radius); + background: rgb(255 255 255 / 3%); + border: 1px solid rgb(255 255 255 / 10%); +} + +:root .wplace-status, +.wplace-theme-classic .wplace-status { + border-radius: var(--wplace-radius); +} + +:root .wplace-alert-base, +.wplace-theme-classic .wplace-alert-base { + border-radius: var(--wplace-radius); + font-family: var(--wplace-font); +} + +:root .wplace-settings-container-base, +.wplace-theme-classic .wplace-settings-container-base { + font-family: var(--wplace-font); + border-radius: var(--wplace-radius); + background: var(--wplace-primary); + color: var(--wplace-text); + box-shadow: var(--wplace-shadow); + backdrop-filter: var(--wplace-backdrop); +} + +/* Button styling for classic theme */ +:root .wplace-btn, +.wplace-theme-classic .wplace-btn { + background: linear-gradient(135deg, #222 0%, #4a4a4a 100%); + border: 1px solid rgb(255 255 255 / 10%); + color: var(--wplace-text); + border-radius: var(--wplace-btn-radius); + font-family: var(--wplace-font); +} + +:root .wplace-btn::before, +.wplace-theme-classic .wplace-btn::before { + background: linear-gradient(90deg, transparent, rgb(255 255 255 / 10%), transparent); +} + +:root .wplace-btn:disabled, +.wplace-theme-classic .wplace-btn:disabled { + box-shadow: none !important; +} + +:root .wplace-btn:hover:not(:disabled), +.wplace-theme-classic .wplace-btn:hover:not(:disabled) { + box-shadow: 0 4px 12px rgb(0 0 0 / 40%); +} + +:root .wplace-btn-overlay.active, +.wplace-theme-classic .wplace-btn-overlay.active { + background: linear-gradient(135deg, #29b6f6 0%, #8e2de2 100%); + box-shadow: 0 0 15px #8e2de2; +} + +/* Button variants - exact upstream main colors */ +:root .wplace-btn-primary, +.wplace-theme-classic .wplace-btn-primary { + background: linear-gradient(135deg, #222 0%, #6a5acd 100%); + color: var(--wplace-text); +} + +:root .wplace-btn-upload, +.wplace-theme-classic .wplace-btn-upload { + background: linear-gradient(135deg, #111 0%, #4a4a4a 100%); + color: var(--wplace-text); + border: 1px dashed var(--wplace-highlight) !important; +} + +:root .wplace-btn-start, +.wplace-theme-classic .wplace-btn-start { + background: linear-gradient(135deg, var(--wplace-success) 0%, #228b22 100%); + color: white; +} + +:root .wplace-btn-stop, +.wplace-theme-classic .wplace-btn-stop { + background: linear-gradient(135deg, var(--wplace-error) 0%, #dc143c 100%); + color: white; +} + +:root .wplace-btn-select, +.wplace-theme-classic .wplace-btn-select { + background: linear-gradient(135deg, var(--wplace-highlight) 0%, #9370db 100%); + color: white; +} + +:root .wplace-btn-file, +.wplace-theme-classic .wplace-btn-file { + background: linear-gradient(135deg, #ff8c00 0%, #ff7f50 100%); + color: white; +} + +:root .wplace-btn.active, +:root .wplace-btn[aria-pressed="true"], +.wplace-theme-classic .wplace-btn.active, +.wplace-theme-classic .wplace-btn[aria-pressed="true"] { + background: var(--wplace-highlight) !important; + color: #000 !important; + border-color: var(--wplace-text) !important; + box-shadow: 0 0 8px rgb(0 0 0 / 25%) inset, 0 0 6px rgb(0 0 0 / 20%) !important; +} + +:root .wplace-btn.active i, +:root .wplace-btn[aria-pressed="true"] i, +.wplace-theme-classic .wplace-btn.active i, +.wplace-theme-classic .wplace-btn[aria-pressed="true"] i { + filter: drop-shadow(0 0 3px #000); +} + +:root .mask-mode-group .wplace-btn.active, +:root .mask-mode-group .wplace-btn[aria-pressed="true"], +.wplace-theme-classic .mask-mode-group .wplace-btn.active, +.wplace-theme-classic .mask-mode-group .wplace-btn[aria-pressed="true"] { + background: var(--wplace-highlight); + color: #000; + border-color: var(--wplace-text); + box-shadow: 0 0 8px rgb(0 0 0 / 25%) inset, 0 0 6px rgb(0 0 0 / 20%); +} + +/* Status styling for classic theme */ +:root .status-default, +.wplace-theme-classic .status-default { + background: rgb(255 255 255 / 10%); + border-color: var(--wplace-text); + color: var(--wplace-text); +} + +:root .status-success, +.wplace-theme-classic .status-success { + background: rgb(0 255 0 / 10%); + border-color: var(--wplace-success); + color: var(--wplace-success); + box-shadow: 0 0 15px var(--wplace-success); +} + +:root .status-error, +.wplace-theme-classic .status-error { + background: rgb(255 0 0 / 10%); + border-color: var(--wplace-error); + color: var(--wplace-error); + box-shadow: 0 0 15px var(--wplace-error); +} + +:root .status-warning, +.wplace-theme-classic .status-warning { + background: rgb(255 165 0 / 10%); + border-color: var(--wplace-warning); + color: var(--wplace-warning); + box-shadow: 0 0 15px var(--wplace-warning); +} + +:root .wplace-status.status-default, +.wplace-theme-classic .wplace-status.status-default { + color: var(--wplace-text); + background: rgb(255 255 255 / 10%); + border-color: var(--wplace-text); +} + +/* Alert styling for classic theme */ +:root .wplace-alert-info, +.wplace-theme-classic .wplace-alert-info { + background: linear-gradient(135deg, #3498db, #2980b9); + box-shadow: 0 4px 12px rgb(52 152 219 / 30%); +} + +:root .wplace-alert-success, +.wplace-theme-classic .wplace-alert-success { + background: linear-gradient(135deg, #27ae60, #229954); + box-shadow: 0 4px 12px rgb(39 174 96 / 30%); +} + +:root .wplace-alert-warning, +.wplace-theme-classic .wplace-alert-warning { + background: linear-gradient(135deg, #f39c12, #e67e22); + box-shadow: 0 4px 12px rgb(243 156 18 / 30%); +} + +:root .wplace-alert-error, +.wplace-theme-classic .wplace-alert-error { + background: linear-gradient(135deg, #e74c3c, #c0392b); + box-shadow: 0 4px 12px rgb(231 76 60 / 30%); +} + +/* Progress bar styling for classic theme */ +:root .wplace-progress, +.wplace-theme-classic .wplace-progress { + background: rgb(0 0 0 / 30%); + border: 1px solid rgb(255 255 255 / 10%); + border-radius: var(--wplace-radius); +} + +:root .wplace-progress-bar, +.wplace-theme-classic .wplace-progress-bar { + background: linear-gradient(135deg, var(--wplace-highlight) 0%, #9370db 100%); +} + +:root .wplace-progress-bar::after, +.wplace-theme-classic .wplace-progress-bar::after { + background: linear-gradient(90deg, transparent, rgb(255 255 255 / 30%), transparent); +} + +/* Header and sections styling for classic theme */ +:root .wplace-header-btn, +.wplace-theme-classic .wplace-header-btn { + background: rgb(255 255 255 / 10%); + color: var(--wplace-highlight); + border-radius: 4px; + font-family: var(--wplace-font); +} + +:root .wplace-header-btn:hover, +.wplace-theme-classic .wplace-header-btn:hover { + background: #222; + color: var(--wplace-text); +} + +/* Font and typography for classic theme */ +:root .wplace-turnstile-overlay, +.wplace-theme-classic .wplace-turnstile-overlay { + background: rgb(0 0 0 / 90%) !important; + border-radius: var(--wplace-radius) !important; + box-shadow: 0 8px 32px rgb(0 0 0 / 40%) !important; + backdrop-filter: var(--wplace-backdrop) !important; + border: 1px solid rgb(255 255 255 / 20%) !important; + color: var(--wplace-text) !important; + font-family: var(--wplace-font) !important; +} + +/* Auto light/dark support for classic theme */ +@media (prefers-color-scheme: light) { + :root .theme-auto, + .wplace-theme-classic .theme-auto { + --wplace-primary: #fff; + --wplace-secondary: #f5f5f5; + --wplace-accent: #007acc; + --wplace-text: #333; + } +} + +@media (prefers-color-scheme: dark) { + :root .theme-auto, + .wplace-theme-classic .theme-auto { + --wplace-primary: #1e1e1e; + --wplace-secondary: #2d2d30; + --wplace-accent: var(--wplace-highlight); + --wplace-text: #fff; + } +} + +/* Border and color styling for classic theme */ +:root .wplace-color-item-name, +.wplace-theme-classic .wplace-color-item-name { + color: #ccc; +} + +:root .wplace-color-swatch, +.wplace-theme-classic .wplace-color-swatch { + border: 1px solid rgb(255 255 255 / 20%); + border-radius: 4px; +} + +:root .wplace-stat-color-swatch, +.wplace-theme-classic .wplace-stat-color-swatch { + border-radius: 3px; + border: 1px solid rgb(255 255 255 / 10%); + box-shadow: inset 0 0 2px rgb(0 0 0 / 50%); +} + +:root .resize-tools button, +.wplace-theme-classic .resize-tools button { + border-radius: 6px; + border: 1px solid rgb(255 255 255 / 20%); + background: rgb(255 255 255 / 6%); + color: #fff; +} + +:root .resize-slider, +.wplace-theme-classic .resize-slider { + background: #ccc; + border-radius: var(--wplace-radius); +} + +/* Text effects and filters for classic theme */ +:root .wplace-color-swatch:not(.active), +.wplace-theme-classic .wplace-color-swatch:not(.active) { + filter: grayscale(80%); +} + +:root .wplace-color-swatch.unavailable, +.wplace-theme-classic .wplace-color-swatch.unavailable { + border-color: #666; +} + +:root .wplace-color-swatch.unavailable:not(.active), +.wplace-theme-classic .wplace-color-swatch.unavailable:not(.active) { + filter: grayscale(90%); +} + +:root .wplace-color-swatch.active::after, +.wplace-theme-classic .wplace-color-swatch.active::after { + text-shadow: 0 0 3px black; +} + +/* Icon colors for classic theme */ +:root .wplace-icon-key, +.wplace-theme-classic .wplace-icon-key { + color: #4facfe; +} + +:root .wplace-icon-robot, +.wplace-theme-classic .wplace-icon-robot { + color: #4facfe; +} + +:root .wplace-icon-speed, +.wplace-theme-classic .wplace-icon-speed { + color: #4facfe; +} + +:root .wplace-icon-bell, +.wplace-theme-classic .wplace-icon-bell { + color: #ffd166; +} + +:root .wplace-icon-palette, +.wplace-theme-classic .wplace-icon-palette { + color: #f093fb; +} + +:root .wplace-icon-globe, +.wplace-theme-classic .wplace-icon-globe { + color: #ffeaa7; +} + +:root .wplace-icon-paint, +.wplace-theme-classic .wplace-icon-paint { + color: #4facfe; +} + +:root .wplace-icon-eye, +.wplace-theme-classic .wplace-icon-eye { + color: var(--wplace-highlight); +} + +/* Form controls and sliders for classic theme */ +:root .wplace-slider, +.wplace-theme-classic .wplace-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); + border-radius: 4px; +} + +:root .wplace-slider::-webkit-slider-thumb, +.wplace-theme-classic .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--wplace-slider-thumb-bg); + border: none; + cursor: pointer; + transition: all 0.2s ease; +} + +:root .wplace-speed-slider, +.wplace-theme-classic .wplace-speed-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); + border-radius: 4px; +} + +:root .wplace-speed-slider::-webkit-slider-thumb, +.wplace-theme-classic .wplace-speed-slider::-webkit-slider-thumb { + border-radius: 50%; + border: 2px solid; +} + +:root .wplace-speed-slider::-moz-range-thumb, +.wplace-theme-classic .wplace-speed-slider::-moz-range-thumb { + border-radius: 50%; + border: 2px solid; +} + +:root .wplace-turnstile-title, +.wplace-theme-classic .wplace-turnstile-title { + color: var(--wplace-text) !important; +} + +:root .wplace-turnstile-hide-btn, +.wplace-theme-classic .wplace-turnstile-hide-btn { + color: var(--wplace-text) !important; + border: 1px solid rgb(255 255 255 / 20%) !important; + border-radius: 6px !important; +} + +:root .wplace-turnstile-hide-btn:hover, +.wplace-theme-classic .wplace-turnstile-hide-btn:hover { + background: rgb(255 255 255 / 10%) !important; +} + +:root .wplace-settings-select, +.wplace-theme-classic .wplace-settings-select { + background: rgb(255 255 255 / 15%); + color: white; + border: 1px solid rgb(255 255 255 / 20%); + border-radius: 8px; + box-shadow: 0 3px 10px rgb(0 0 0 / 10%); +} + +:root .wplace-settings-option, +.wplace-theme-classic .wplace-settings-option { + background: #2d3748; + color: white; +} + +:root .wplace-stat-colors-grid, +.wplace-theme-classic .wplace-stat-colors-grid { + background: rgb(0 0 0 / 20%); + border-radius: 4px; +} + +:root .resize-overlay, +.wplace-theme-classic .resize-overlay { + background: rgb(0 0 0 / 80%); +} + +:root .wplace-overlay, +.wplace-theme-classic .wplace-overlay { + background: rgb(0 0 0 / 80%); +} + +:root .wplace-settings-error, +.wplace-theme-classic .wplace-settings-error { + background: rgb(255 0 0 / 40%) !important; +} + +/* Settings sections and batch controls for classic theme */ +:root .wplace-settings-section-wrapper, +.wplace-theme-classic .wplace-settings-section-wrapper { + background: rgb(255 255 255 / 10%); + border-radius: var(--wplace-radius); + border: 1px solid rgb(255 255 255 / 10%); +} + +:root .wplace-batch-controls, +.wplace-theme-classic .wplace-batch-controls { + background: rgb(255 255 255 / 10%); + border-radius: var(--wplace-radius); + border: 1px solid rgb(255 255 255 / 10%); +} + +:root .wplace-overlay-opacity-slider, +.wplace-theme-classic .wplace-overlay-opacity-slider { + background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); + border-radius: 4px; +} + +:root .wplace-speed-value, +.wplace-theme-classic .wplace-speed-value { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + border-radius: 8px; + color: white; + box-shadow: 0 3px 10px rgb(79 172 254 / 30%); + border: 1px solid rgb(255 255 255 / 20%); +} + +/* Additional elements for classic theme */ +:root .wplace-color-divider, +.wplace-theme-classic .wplace-color-divider { + background: rgb(255 255 255 / 10%); +} + +:root .wplace-settings-select option, +.wplace-theme-classic .wplace-settings-select option { + background: #2d3748; + color: white; +} + +:root .wplace-settings-description, +.wplace-theme-classic .wplace-settings-description { + color: rgb(255 255 255 / 70%); +} + +:root .wplace-settings-apply-btn, +.wplace-theme-classic .wplace-settings-apply-btn { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + color: white; + border-radius: 8px; +} + +/* Dual control layout styling for classic theme */ +:root .wplace-dual-control-compact, +.wplace-theme-classic .wplace-dual-control-compact { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + margin: 8px 0; + flex-wrap: wrap; +} + +:root .wplace-slider-container-compact, +.wplace-theme-classic .wplace-slider-container-compact { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 160px; +} + +:root .wplace-speed-slider-container-compact, +.wplace-theme-classic .wplace-speed-slider-container-compact { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 160px; +} + +:root .wplace-input-group-compact, +.wplace-theme-classic .wplace-input-group-compact { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +:root .wplace-input-btn-compact, +.wplace-theme-classic .wplace-input-btn-compact { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + color: white; + border: none; + border-radius: 4px; + width: 22px; + height: 22px; + cursor: pointer; + font-weight: bold; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + +:root .wplace-input-btn-compact:hover, +.wplace-theme-classic .wplace-input-btn-compact:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0,0,0,0.3); + background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%); +} + +:root .wplace-input-btn-compact:active, +.wplace-theme-classic .wplace-input-btn-compact:active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + +:root .wplace-number-input-compact, +.wplace-theme-classic .wplace-number-input-compact { + background: rgba(255,255,255,0.1); + border: 1px solid rgba(255,255,255,0.2); + border-radius: 4px; + color: white; + padding: 4px 8px; + font-size: 12px; + width: 50px; + text-align: center; + transition: all 0.2s ease; + height: 22px; +} + +:root .wplace-number-input-compact:focus, +.wplace-theme-classic .wplace-number-input-compact:focus { + outline: none; + border-color: #4facfe; + box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3); + background: rgba(255,255,255,0.15); +} + +:root .wplace-input-label-compact, +.wplace-theme-classic .wplace-input-label-compact { + color: rgba(255,255,255,0.8); + font-size: 11px; + margin-left: 4px; + white-space: nowrap; +} + +:root .wplace-batch-size-header, +.wplace-theme-classic .wplace-batch-size-header { + margin-bottom: 8px; +} + +:root .wplace-batch-size-label, +.wplace-theme-classic .wplace-batch-size-label { + color: var(--wplace-highlight); + font-weight: 500; + font-size: 13px; +} + +/* Cooldown control styling for classic theme */ +:root .wplace-cooldown-control, +.wplace-theme-classic .wplace-cooldown-control { + margin: 12px 0; +} + +:root .wplace-cooldown-control label, +.wplace-theme-classic .wplace-cooldown-control label { + color: var(--wplace-highlight); + font-weight: 500; + font-size: 13px; + margin-bottom: 8px; + display: block; +} + +/* Resize dialog styling for classic theme */ +:root .resize-container, +.wplace-theme-classic .resize-container { + background: #000; + border: 1px solid #fff; + border-radius: 12px; + box-shadow: 0 0 20px rgb(0 0 0 / 50%); +} diff --git a/Extension/themes/neon.css b/Extension/themes/neon.css new file mode 100644 index 0000000..a6d5795 --- /dev/null +++ b/Extension/themes/neon.css @@ -0,0 +1,519 @@ +/* WPlace Auto-Image Bot - Neon Theme */ +/* Retro cyberpunk aesthetic with green neon glow effects */ + +.wplace-theme-neon { + /* Neon theme colors - matching upstream main neon theme */ + --wplace-primary: #1a1a2e; + --wplace-secondary: #16213e; + --wplace-accent: #0f3460; + --wplace-text: #00ff41; + --wplace-highlight: #ff6b35; + --wplace-success: #39ff14; + --wplace-error: #ff073a; + --wplace-warning: #ff0; + + /* Neon UI properties */ + --wplace-radius: 0; + --wplace-border-style: solid; + --wplace-border-width: 2px; + --wplace-border-color: #00ff41; + --wplace-shadow: 0 0 20px rgb(0 255 65 / 30%), inset 0 0 20px rgb(0 255 65 / 10%); + --wplace-backdrop: none; + --wplace-font: 'Press Start 2P', monospace, 'Courier New'; + + /* Z-index layers */ + --wplace-z-overlay: 10000; + --wplace-z-alert: 10002; + --wplace-z-settings: 10002; + + /* Feature toggles */ + --wplace-scanline: 1; + --wplace-pixel-blink: 1; + + /* Icon colors - neon variants */ + --wplace-icon-primary: #00ff41; + --wplace-icon-secondary: #39ff14; + --wplace-icon-palette: #ff6b35; + + /* Additional UI colors - neon variants */ + --wplace-danger: #ff073a; + --wplace-danger-dark: #cc0531; + --wplace-muted-text: #00ff4180; + --wplace-highlight-secondary: #fa0; + + /* Text variants - neon style */ + --wplace-text-secondary: #00ff41dd; + --wplace-text-muted: #00ff41bb; + --wplace-text-dim: #00ff4199; + --wplace-text-faded: #00ff41cc; + + /* Background variants - neon style */ + --wplace-bg-input: rgb(0 255 65 / 15%); + --wplace-bg-subtle: rgb(0 255 65 / 8%); + --wplace-bg-faint: rgb(0 255 65 / 5%); + --wplace-bg-ghost: rgb(0 255 65 / 3%); + --wplace-bg-whisper: rgb(0 255 65 / 2%); + + /* Border variants - neon style */ + --wplace-border-subtle: rgb(0 255 65 / 40%); + --wplace-border-faint: rgb(0 255 65 / 25%); + --wplace-border-ghost: rgb(0 255 65 / 15%); + --wplace-border-ultra-faint: rgb(0 255 65 / 8%); + + /* Shadow variants - neon style */ + --wplace-shadow-drag: 0 12px 40px rgb(0 255 65 / 60%), 0 0 0 2px rgb(0 255 65 / 80%), 0 0 20px rgb(0 255 65 / 30%); + --wplace-shadow-notification: 0 4px 12px rgb(0 255 65 / 40%), 0 0 15px rgb(0 255 65 / 20%); + --wplace-shadow-slider-thumb: 0 3px 6px rgb(0 255 65 / 50%), 0 0 0 2px var(--wplace-icon-primary), 0 0 10px rgb(0 255 65 / 30%); + --wplace-shadow-slider-hover: 0 4px 8px rgb(0 255 65 / 60%), 0 0 0 3px var(--wplace-icon-primary), 0 0 15px rgb(0 255 65 / 40%); + + /* Animation colors - neon style */ + --wplace-pulse-start: rgb(0 255 65 / 80%); + --wplace-pulse-mid: rgb(0 255 65 / 0%); + --wplace-pulse-end: rgb(0 255 65 / 0%); + + /* Slider colors - neon style */ + --wplace-slider-thumb-bg: #00ff41; +} + +/* Neon-specific styling overrides */ +.wplace-theme-neon #wplace-image-bot-container { + background: var(--wplace-primary) !important; + border: 2px solid var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon #wplace-stats-container { + background: var(--wplace-primary) !important; + border: 2px solid var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important; + font-family: var(--wplace-font) !important; + position: fixed !important; + overflow: hidden !important; + z-index: 9998 !important; +} + +.wplace-theme-neon .wplace-header { + background: var(--wplace-secondary) !important; + border-bottom: 1px solid var(--wplace-text) !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 10px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon .wplace-section { + background: rgb(22 33 62 / 50%) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; +} + +.wplace-theme-neon .wplace-status-section { + background: rgb(22 33 62 / 50%) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; +} + +.wplace-theme-neon .wplace-section-title { + color: var(--wplace-text) !important; + text-shadow: 0 0 8px var(--wplace-text) !important; + text-transform: uppercase !important; + font-family: var(--wplace-font) !important; + font-size: 10px !important; +} + +.wplace-theme-neon .wplace-btn { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + font-size: 9px !important; + text-transform: uppercase !important; +} + +/* Pixel blinking and neon glow animation for buttons */ +.wplace-theme-neon .wplace-btn:hover:not(:disabled) { + box-shadow: 0 0 15px var(--wplace-text), inset 0 0 15px rgb(0 255 65 / 10%) !important; + animation: pixel-blink 0.5s infinite, neon-glow 1s ease-in-out infinite alternate !important; +} + +.wplace-theme-neon .wplace-btn-start { + background: var(--wplace-secondary) !important; + border-color: var(--wplace-success) !important; + color: var(--wplace-success) !important; + text-shadow: 0 0 8px var(--wplace-success) !important; +} + +.wplace-theme-neon .wplace-btn-stop { + background: var(--wplace-secondary) !important; + border-color: var(--wplace-error) !important; + color: var(--wplace-error) !important; + text-shadow: 0 0 8px var(--wplace-error) !important; +} + +.wplace-theme-neon .wplace-btn-upload { + background: var(--wplace-secondary) !important; + border: 1px dashed var(--wplace-highlight) !important; + color: var(--wplace-highlight) !important; + text-shadow: 0 0 8px var(--wplace-highlight) !important; +} + +.wplace-theme-neon .wplace-btn-select { + background: var(--wplace-secondary) !important; + border-color: var(--wplace-highlight) !important; + color: var(--wplace-highlight) !important; + text-shadow: 0 0 8px var(--wplace-highlight) !important; +} + +.wplace-theme-neon .wplace-btn-file { + background: var(--wplace-secondary) !important; + border-color: var(--wplace-warning) !important; + color: var(--wplace-warning) !important; + text-shadow: 0 0 8px var(--wplace-warning) !important; +} + +.wplace-theme-neon .wplace-progress { + background: rgb(0 0 0 / 80%) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; +} + +.wplace-theme-neon .wplace-progress-bar { + background: linear-gradient(90deg, var(--wplace-success) 0%, var(--wplace-text) 100%) !important; + box-shadow: 0 0 10px var(--wplace-success) !important; +} + +.wplace-theme-neon .wplace-stat-value { + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; +} + +.wplace-theme-neon .status-success { + background: rgb(57 255 20 / 10%) !important; + border-color: var(--wplace-success) !important; + color: var(--wplace-success) !important; + box-shadow: 0 0 15px var(--wplace-success) !important; + text-shadow: 0 0 8px var(--wplace-success) !important; +} + +.wplace-theme-neon .status-error { + background: rgb(255 7 58 / 10%) !important; + border-color: var(--wplace-error) !important; + color: var(--wplace-error) !important; + box-shadow: 0 0 15px var(--wplace-error) !important; + text-shadow: 0 0 8px var(--wplace-error) !important; +} + +.wplace-theme-neon .status-default { + background: rgb(0 255 65 / 10%) !important; + border-color: var(--wplace-text) !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; +} + +/* Settings dialog neon styling */ +.wplace-theme-neon #wplace-settings-container { + background: var(--wplace-primary) !important; + border: 2px solid var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 30px var(--wplace-text), inset 0 0 30px rgb(0 255 65 / 10%) !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon .wplace-settings-header { + background: var(--wplace-secondary) !important; + border-bottom: 1px solid var(--wplace-text) !important; +} + +.wplace-theme-neon .wplace-settings-header h3 { + color: var(--wplace-text) !important; + text-shadow: 0 0 10px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + font-size: 16px !important; + text-transform: uppercase !important; +} + +.wplace-theme-neon .wplace-settings-close-btn { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; +} + +.wplace-theme-neon .wplace-settings-close-btn:hover { + background: var(--wplace-error) !important; + box-shadow: 0 0 15px var(--wplace-error) !important; +} + +.wplace-theme-neon .wplace-settings-section-wrapper { + background: rgb(22 33 62 / 30%) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; +} + +.wplace-theme-neon .wplace-settings-select { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + font-size: 11px !important; +} + +.wplace-theme-neon .wplace-settings-section-label { + color: var(--wplace-text) !important; + text-shadow: 0 0 8px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + font-size: 12px !important; + text-transform: uppercase !important; +} + +.wplace-theme-neon .wplace-speed-value { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 8px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important; +} + +.wplace-theme-neon .wplace-overlay-opacity-value { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; +} + +/* Neon slider styling */ +.wplace-theme-neon .wplace-slider, +.wplace-theme-neon .wplace-speed-slider, +.wplace-theme-neon .wplace-overlay-opacity-slider { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important; +} + +.wplace-theme-neon .wplace-slider::-webkit-slider-thumb, +.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb, +.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + border-radius: 0 !important; + background: var(--wplace-slider-thumb-bg) !important; + border: 2px solid var(--wplace-text) !important; + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 0 8px rgb(0 255 65 / 50%) !important; +} + +.wplace-theme-neon .wplace-slider::-webkit-slider-thumb:hover, +.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb:hover, +.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb:hover { + transform: scale(1.1); + box-shadow: 0 0 15px var(--wplace-text) !important; +} + +/* Scanline animation for neon theme */ +.wplace-theme-neon #wplace-image-bot-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, var(--wplace-text), transparent); + z-index: 1; + pointer-events: none; + animation: scanline 3s linear infinite; + opacity: 0.7; +} + + +.wplace-theme-neon #wplace-stats-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, var(--wplace-text), transparent); + z-index: 1; + pointer-events: none; + animation: scanline-stats 4s linear infinite; + opacity: 0.7; +} + + +/* Text glow animations */ +@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 scanline-stats { + 0% { + transform: translateY(-100%); + } + + 100% { + transform: translateY(300px); + } +} + +/* Dual control layout styling for neon theme */ +.wplace-theme-neon .wplace-dual-control-compact { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + margin: 6px 0; + flex-wrap: wrap; +} + +.wplace-theme-neon .wplace-slider-container-compact { + display: flex; + align-items: center; + gap: 6px; + flex: 1; + min-width: 160px; +} + +.wplace-theme-neon .wplace-speed-slider-container-compact { + display: flex; + align-items: center; + gap: 6px; + flex: 1; + min-width: 160px; +} + +.wplace-theme-neon .wplace-input-group-compact { + display: flex; + align-items: center; + gap: 3px; + flex-shrink: 0; +} + +.wplace-theme-neon .wplace-input-btn-compact { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + width: 18px; + height: 18px; + cursor: pointer; + font-weight: bold; + font-size: 10px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + text-shadow: 0 0 5px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon .wplace-input-btn-compact:hover { + box-shadow: 0 0 10px var(--wplace-text) !important; + animation: pixel-blink 0.5s infinite !important; +} + +.wplace-theme-neon .wplace-number-input-compact { + background: var(--wplace-secondary) !important; + border: 1px solid var(--wplace-text) !important; + border-radius: 0 !important; + color: var(--wplace-text) !important; + padding: 2px 4px; + font-size: 10px; + width: 40px; + text-align: center; + transition: all 0.2s ease; + height: 18px; + font-family: var(--wplace-font) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; +} + +.wplace-theme-neon .wplace-number-input-compact:focus { + outline: none; + box-shadow: 0 0 10px var(--wplace-text) !important; + background: rgb(0 255 65 / 15%) !important; +} + +.wplace-theme-neon .wplace-input-label-compact { + color: var(--wplace-text) !important; + font-size: 9px; + margin-left: 3px; + white-space: nowrap; + text-shadow: 0 0 5px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + text-transform: uppercase; +} + +.wplace-theme-neon .wplace-batch-size-header { + margin-bottom: 6px; +} + +.wplace-theme-neon .wplace-batch-size-label { + color: var(--wplace-text) !important; + font-weight: 500; + font-size: 11px; + text-shadow: 0 0 8px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + text-transform: uppercase; +} + +/* Cooldown control styling for neon theme */ +.wplace-theme-neon .wplace-cooldown-control { + margin: 8px 0; +} + +.wplace-theme-neon .wplace-cooldown-control label { + color: var(--wplace-text) !important; + font-weight: 500; + font-size: 11px; + text-shadow: 0 0 8px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + text-transform: uppercase; + margin-bottom: 6px; + display: block; +} + +/* Resize dialog styling for neon theme */ +.wplace-theme-neon .resize-container { + background: #1a1a2e !important; + border: 3px solid #00ff41 !important; + border-radius: 0 !important; + box-shadow: 0 0 30px rgb(0 255 65 / 50%) !important; + font-family: 'Press Start 2P', monospace !important; +} + From 9fa942a3852a1355d589488ce321a1db86d2a55b Mon Sep 17 00:00:00 2001 From: "Others...14" Date: Mon, 8 Sep 2025 16:29:33 +0700 Subject: [PATCH 10/18] Extension/icons/ --- Extension/icons/icon32.png | Bin 112618 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Extension/icons/icon32.png diff --git a/Extension/icons/icon32.png b/Extension/icons/icon32.png deleted file mode 100644 index 003ea1e54b8128cf1058cb4b805fc79aed3213f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112618 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 XgjSII!zHqx^g+l Date: Mon, 8 Sep 2025 16:29:50 +0700 Subject: [PATCH 11/18] Extension/icons --- Extension/icons/icon128.png | Bin 112618 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Extension/icons/icon128.png diff --git a/Extension/icons/icon128.png b/Extension/icons/icon128.png deleted file mode 100644 index 003ea1e54b8128cf1058cb4b805fc79aed3213f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112618 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 XgjSII!zHqx^g+l Date: Mon, 8 Sep 2025 16:33:02 +0700 Subject: [PATCH 12/18] Extension/icons/ --- Extension/icons/icon16.png | Bin 112618 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Extension/icons/icon16.png diff --git a/Extension/icons/icon16.png b/Extension/icons/icon16.png deleted file mode 100644 index 003ea1e54b8128cf1058cb4b805fc79aed3213f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112618 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 XgjSII!zHqx^g+l Date: Mon, 8 Sep 2025 16:33:19 +0700 Subject: [PATCH 13/18] Extension/icons/ --- Extension/icons/icon48.png | Bin 112618 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Extension/icons/icon48.png diff --git a/Extension/icons/icon48.png b/Extension/icons/icon48.png deleted file mode 100644 index 003ea1e54b8128cf1058cb4b805fc79aed3213f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112618 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 XgjSII!zHqx^g+l Date: Mon, 8 Sep 2025 16:34:56 +0700 Subject: [PATCH 14/18] Add files via upload --- Extension/icons/icon128.png | Bin 0 -> 61717 bytes Extension/icons/icon16.png | Bin 0 -> 61717 bytes Extension/icons/icon32.png | Bin 0 -> 61717 bytes Extension/icons/icon48.png | Bin 0 -> 61717 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Extension/icons/icon128.png create mode 100644 Extension/icons/icon16.png create mode 100644 Extension/icons/icon32.png create mode 100644 Extension/icons/icon48.png diff --git a/Extension/icons/icon128.png b/Extension/icons/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..39f31066820c76eb51a4d33d33d5783590e6e99c GIT binary patch literal 61717 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�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�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�YGkH5*t8fn6359sP*>Jx~63onAkjIm8BT>7_Bo>lDsq68ou zhynsHUO8c>>@Q*n>kI)r41x@7ob~!12^cXMG6fAgm*zs8t_j1xYR13nUAt|j^)XU7 zFAfR!Ry*VdO;z|9sFwa4EVJ|?dX$aFjiO85e;TDvfia2$i;w(Y1;v!TH0%oZ*z6)1`LiEN!a<%~-SaarpshLB!{Bh0ryXu* zDSJJkH(mC%h*pxMGl z&Z;n3ae33SYd&tr78#kO1Ro2yec3?&;D>UR!qzg%4e5yCk@`P%Z)hsmzVn8 zzO&+kq5LUo>M_VT$V&9|$VFSRX<@&wN&)o0Q(}H!6EP%fik0Z<%3ORVPx{eTtVal4FU5GtFIx5%W4-V^K;@fdz(Bnw(%ZTW-;xbjX$6LgWgS)RF z(HzB?qx*Lvjiu}boxRvH(G?PUCGp(#-7rzTaE3RPvi$D7IFpI}bh-y~EqU}siV7S? zB6EI>OvK)^Feeah-qj2@Y78O~{jR4|Pb^k=g=ZLs+HvNW<(%am6c`wJQFK=4iHEZ4 zhNtDS@DlqsXFe*N2Qs7iUr>^z$DL?Q$mPr-4FG$}n;^uY=Z2enrpO>|01XAZ1gz$x zF-ELvE9(7PA+}^Z8Ue7ZmkN3tehuMw?pr;%9FZbz{CT+zuZ3=B@&ts{RE|Tsee1TF zxCV)@wkeeUkgGw`)ObtH_5+Uf_w{mQIw8*bV<^J>eY{<&SjfMrkUY{9loq6wQm^Rw z^RvK0fVX|ndy@~7>eMJ&y?OYUBZFEaPz->C^+jCJWj&p1c4j>>ZNqf&79iT0C_nL( zbru*R(T7OE3SxF*S2F$6L#~ZC>M8;;QYuw4#M1X6wb+@6dI}{AYl7;+1A4QY6fb|; zjx*F+Lp?P|@s(FK?c09;RJ0Fz;l;^rmd{_1=hQ+9PiB45(zl;f;Rbyu)M}fC_s z96UkQii}BP-g2GbNlhIe*ZdBy?T-ZwkAVHWuN77AhdAdy`o0=!JF93-CdW!7Rw#N~ zD44u^R6ntPU+u7rhz9B&wVYxngVD`>oBxRfbcDJ&;y{S@tff3FIw^aTqGs)PFgvww zZC@$v{VS5r_?gK^*FAR716wN8)9`zVE-Ye5wM_CN6*;w1rshv?QDK9pb8}B127S2&nrOytGQv>SXRg=0`UC3ugf+dknhOUN14DG|X zx!#PvXb97ioq-ibMvCB8-rw=fC0{RA&N|Q1%NVD8uzDWsxJ#1@pe|LtEN+R}{upb+ zM29_6zaFWWnF`0Sra!?NznuR}7nI4OE@#N5=n6VUJl2JwBsSWep{4Stv={ zgu#Rqj}fG!lp|NMddyUdxNk_(0y_mY412S+zfm+~Mib>s>Uz$T4h%QIljf~c$O9gl zCB}FMXJ5-^1F>;t6k%^zWrZoQV&s}}kO0YI`Y~Z}VT|_SDMd)IBhXl(56IkNP5v;a zgm0Hd+{-Qihsa{MqMw1{gV+0ormnf04e!^%w+n6O6bnB4iv{m(MW+;4PuTLNvI`ot zOVF@A3D%r6Oe?fm7*sz72<68^WrX&jswZjtDbJ?4IbS+Bi~P`aquk5 z+pVEkFyp4`{5|24DCQC#UV*0u?wX!?(Y9{Mw)pO>e)a%de$=H#i3p5=aRF`q#9)}Q zud$<~@N;f%xF_(4WN2*|&f8_7GsqGV<#QorIPxVn4z}7tef}N5mLz|sl<8r5@LqIs zk%qX&|8bc zkXs{&>wo(V(jV!36Zoe7jo;SaxR^B>afY1`JDS%>+^ueUCg=b+XrI~y zknIAs5YjI0=MCw)fC?S!7ue3z`TQa8shk9J`tprU`!_VR_PQZveLhmX-nAkPayBli zhE+JqL%cvlUd6VMEzr7x;0xZBkN`fbkX|q95&t@hd%LmG-hqGQ`+humKXW%>K0Vr% zpD1Y`JFqvXVr1ZYkAfBEw8Dh`jcsp^ZRg}j$DX4+mA@@B7#t1>FBy~4lZ5jS*?R_v zJDvZyWuY4J*jw6Ca(8eAWZ&+XR+R(}?+6CseD@SG4kBZ~W>*VE+*##&G=4XF#DD7- zg+D3)^UNZHNa*B|E}PR5rLmx>#=lfaw#w1!^8%<426(uUKYjfaLnBFPFHcsyvvt%9 zdZd4!`r&=L=Fa_6@E-kx@G5v|dsx7b4-FMNjc}W8dg=VMmqfCZyhUp?{?EiFkg6js z9QG|wCqne`!z)Se^_L@6Ckf>$g8Pxbn@-DSm^aDs0xFEe(J1Xs?tinWkG%8e`87RF zqb9;3+Zw_Ls#f0pb!Z!~;xlCZ)I{9K9dJxU?6?Pq@u4pdO#I01d>mNl06oOtxK9K_ zKg0&t?-?gpQmQ1LX?QzZLd-H&+!BumCOEHS(-%y+uNE&sv%S0yZEMAy@ilzRPwa&< zhcbf;5#OiCLx{CJL#L67Mx}Gc3K2~E)b!PX<;H+?bn+_zEnO%DPl8mt1~*so&Z*PZ z>5ifJ!0GBNuxDDb*qyxiq=^fikPQdoAY$1KSap9cZhatsjG{ZE88s3J^Kia#V+P9-AO2N8h8 zeIl+NAiHOi?ZqtjgJ!?z=lu9k@g))SpzTL|V z5p&eTR!`FE*Gxo!Wr{$3n(V)9cO0-pP2$nc`ermal!VUw}5LIV^v^t6m zec7XRAv5A0NS~Po-0Yo2f-b{YzkMIMZid9&Knx%#(8Jf|>k{$Zu-VNma|>Y;FUP31 z=tC6uiJiNL<|+jwp9S9ikX*pLFSUezWIK3L^Te6|dw_A~ICt{(sxVi8{{=_B|4{=~ zTQg-V$%4UH65|QEkX1MQ?2@slMC&|Ni;_7jSN>jJnKNZuy%xh=e9ZKAbyJmA4K{Qe z$_{W9_9tFqA-@zn&GSh!#YzNyo>R>u_+4qZpp5E7sn|Xa3Vx)8lN3H~Hs{$=hEFJn zfKU)Kit!%J@-_l(#E4o6up}f6r$#t9Kr6Fs7ynE z7$e5%omF0PWBJ%rGsWpK%`?*ar6GKP0~Q)V`gxEn2WJAKo=X~@3B6g+0}9QeM(H4; zc4jNFv9f2M4ogZlP)r1)c9p&P`*a2kf>D?_TO$J0S3_ePz??rMv*9Kn%no^YQ|m_k z|2Ylx+xFJetOnnXV$vu`i^jrqmbmCG$XukP9ra5J7g{3B-B3Mot1w35?$EFMY;R=i z&u0FY9g62V#kSwE*>C zQ5vc6A0<{%mE*d=wDus%<~;Pj(UoXcA+wSOIaGbc3=h~ zBr}qvog#>wnU4yj!)UenY(MbxQh}JSXn!EP5e=2NqRRF3Rgs;sYI|OyNPNvmT?T|T zQxkv3pQH%2UA^1$;5DShMRm$D-<7|CIH)oBj}xPRXmtrorVNMb%e^$Wch}6G)0!y+ zoW1a;M9!;S=h~Z>35j!jX2H=^nl(Kyygo*F*ABBI(rshNL(nbvp;fH5X69*&CP#`Q z7gpbuAFSP)$vm>@ug9)pPXDCSisVfRSB$6Vet6^hfk09pCqHJVshXqc6(~-g+f|`G z318vMR@Y+X1#dru^}Nz5XvfI>CJ=^%;iRbIR|IJ#YxhlZ_CaMCCD3F7rzA}qIH+h; zz&%&@rgAdDj>KpwHARM{y#BD2XnsaplI@}r2dY!>oot1XB+?3kr|dZFR`*N9fR6R! z1cg(ZS$BbXv>#%{qDVq67nC@10V)obTm)zXjY!V^JF>J?N%D=uv5zH)WS=Cs*5KCE z+=B1h8enzlOQHD?K)~gO5 zQ-CthPD1~JqxS0kQxBIxIf;xC{soCL!*wE!^U%L|qmH0UKR%3>{)LT+0{WL5=|0(& zmuG@fT;j9&*>6Y3Ry^}v*`1mck`Vtjm#$VL35-^dOg>^Y<%xBJQ}&%D4x*a7g>~s@ z*N{!t1@;HB&Y3DFevyB+!mw|-OUy$2t;Wl15}W(2e$K)+%lCP0rmtG^uL-4M&m$>C zhyI+G;$df2SHcC%-}z$EK+6nOH0GR~(yJPL`-eHrj>IAj<`xGAZxPMwb$nI)qw0p% z72X>cYEX1cvr0^5?KJc=CZ%BYuNTH2&Nir!LU%lZ-*Qv!U8;$4W+>f;3d{y54nAje z4S$?R)i^TMJs3;*i4v)1K~v3`DJ8XzQ2w~1!QV@;e1S6HE>XQTx1F9>69tf=PJ0-n zn3XCcjM=+E&A4-nIbNnp076b65!V_xI(eg`{M7iQIYy!IF^a|#JGq4E`y9wn@5yjv z#NZHi&HZxr=;&7LB4IP~+^^K{{^^QfPos;6gcIGi*9EsBr95)mZ@J*oW^nVt9LV&FVoy}A0 z{rnICaGe~|B2?fkWiB{RG!iCUe7(Y*uk++80_DO7$~($Xi3DPC+61o9xaN=Qao6Gq z39;GrZuL9ydrc|f3$?sn-`-|FhUe5?)>tCN57KwW@cfxpoBnd=d{Y$7c@D`{qT*$A z_wDE~r7G0wVv&eb&+mqZZ6%hIt)l4O>gnO@{}7>#f1U;L71?xVysdmXiE_%^27e8t z55l^lrJVqWg&)2qs4!LT%AahmQL{_Sm2uo(w8F(sU3%IH1WiHx>ve0dUaI_2G!}U= zCz~by%Om0I2g?e4*1B2f!hVTDf$opGRhm$^&}uNNSfRYbhu7Ce6x9?XDoHq)AfcG` zZ~~^NOKapQolfWJe17ACp+4veUV9 z$SU>kWoU=OPAXLXwRTD-0EiXYdg5*o@yHLpjI20{kUz?+-|59s)W8fxT?*E*O0Gpz zKgX9H>AzamNtU8ZgxT9({kh*TxF;=_GCX>WupP&I#&LP=C0J@ik(ANfuRL`O{_0jP zBdQW82mh~47jOi)$B>5}*E}&Ql&9n{d7-IYM`DVc!H85n-@TTHY8)G)J0lwpN!hl2 z|Cy-eOLUs}f`l51mxG&ABl1tBLXG{lpw0Ftqo;moS(V8UWIOj2tqbire3!B0zvZr)*jR?N5c!QCV|p{ zJmc+YZpE%hbTI>ZG$M*ybD@D(x5-nQ=JUc6RYP>HZ9V2tX^(Tn^Pz&M= zdH~60^=eZSr?_$Ka1oAd3S7SwvXm4i+uHnD59m7mb))34JYR*40)>H_X#g^YA@?s1 z9Oy6^`3$wr?`KA4$Gr7yP0GPDR~{+#x_WzmP!TD_k3M2R%J%qdco3z-N2h|nRnp~% zo(yw~a1h(y4#bzRQ02QrZ#mY`vsjRS6kIctR{P%Ii}P~p|<3}E>y@$ zvJ?_bs;F~a+HnVO$3HCDUjLhMm)3L3S;hYc8Fdu=SFn+aB-DM<1uhKdBN0>xg=(pmm68H)K2j=U%inRva;fVDfc|Gu zP+S5@8tfNeg$`c{LmI-TL3)wzProyMynMji;R#uStrfSG6oC63UC{px=l%Hpr0aR> zZ`Tk1Q#!|Cp6jbU{A>Dam7@8dEu#sPeUo@BIMkv;7W0Hk#xhWDz^eRF5-%}`el}G9 zq8C;YTzbsZ@Qbo<`?cWR!dx#X^H$bw>d>~ce(yMprEu_9NQL}mGZ@+B$E#IkJ(L}0 zIUKwk{*xk%xOwv^+lZw(^%&uCY)P+5&hArG7zMz~6!p2<-`5VxXipyJY|W(}@GATh zR|IZx8R-eWkXft&$`!FPM#Kwl#8M0Ar!KgHvZsAkaRWvMofM`WY6emi8~gzE;wjKa6K>iH=g_{OFGLB@h{OTw-(|tOL~#DZIR1#I8xLmYWn~u z2rt(HAISgWye`v2R>_uYtBl^(mBW@CX*^X=f{F301+Yc9B3;NE(LC!V}*^znt#{o4u3qw)`gAywA?0AGgLpe3Vrg~#f?DgVuXdz?X$t27it~w?$ zJqSfq7=$Q>A4cxZ3LdJD7j^u@{Ih7nO-bR=K#?gjCLe2xi9D?Y37MeD7+ZJkk=M-Sn&Oj_jTE>`Xv9 zSxkK+_$BOz9zDiDuF9!H$!bz8TU#vGs;_N~D$~wRMPp9WR{28dAx|V6*E&u==ln*6 z{RXb?$so}=KVlb71vg1TltAs`eUh1LlY;G2{!<`NuFaQEa@f+@dzQEP*jIDHlTw1I zjQ;tLOJj%%m2av^=(%shAC&OFlN@%SJi-&1t6gocjk z`(d$~KOMR7o4WOgDKbyK$b)jY=v#d7@wT_CH=Ord8flAp0zOYB2dTX@duGF~<)n6>RuX&d#vgH&G{j0n#FAnIdp^^0N}Dg}NsOUzk)0nt1ykWGkbF9k zeHvH!bPcZzD8s8RIxJP*FFLw+57BA5RGe*9Z6dBY^HGy9f z`K>-wdljwih1>RajZw%MbL5+M;60+BMjq*Sh99KL7Fn*`5+z(KptLv|{1nn|`9K zG|pmm2LJsmiFH<=4xH6eYD^gjZroTKo-cAVV*TIr@}_6j2)uPnmu}(nvJm`4ft9 zC6&KUuCX?aAZcKgC}T_kNuAJ!T}eonDY!W9)!1zaulyW#<$q{{4`V+~M`ZXdmpK>F zM&n@c05IL9bJaGQ#6B@J!pANTXiD@Z&Q`!ehYvH`tXc}J(G$DAKh}Et}k0!V|>uXb0VT%MdE%w zF+{xVef`K0nljk`ho73Rzvyc77brsAQtzorWdYquPT=PZDnGr%CW1y_b*L@buZjaw zW*4CYxfT&0O0<&88j)kE-iy}+HrylfxWM9(DSQy@*SFWz12jjhnX`ytd+r3$%gc|5 z(os_c*$B<~HhgcniFi;cWQFCDjb=YiXEdWXo&8bKzl#uD;wx}sPCHfPJP55`7zbTe z4U;HbDV)3#S(APeBT?xxOwFt~d?8@DV8~L;-+oMWS<{&O7T60ZsSV3Q3sQ~!a3F(? za;{0qDEshP(bO@x<1{8e``FQda#}C(^5es(6(wwj!_*g;yQ^QmXGGC;3tCVkpPEm! zhDMV7fVKE*PhuA3xzu`qhR+8HW$|LBE>ig?6hy2;=J| z5vHymeEYMPOV!fV9@~ok7z;}p55(PAzw^46s#4&RK3h^@pm!w!$&tF!o{Z*{)Tk%q zmw0ZG{pz{$-U>bm6cmk!s3JmmiG7x-(to`fe4z71grGd7Dm^tIp)OpvUI0uBkUyk& z%^Js*;D{v+O%taPn`|ZSW%VyI+sx)byw#8mL!WuEswV!lb zc>|E)WCy$P?x##(>_+DTxK+^X48>+by3Z8hPpf~>f04s>YG~J_3KAF;7e?!`Da)(U zVML%-`r#l%T^?A8?=e-^Hs+?AI_%j{4lQJ<`n2zc%G+;6JYric5!AdSd(WDc6Q=nz zLS|D~&ljNS$9DzE#-|8qTXY|p8_aFEtZ=DP4bfI+QXdXxEyt@ViCN1H9k^K6)_P6i z2T(tXx7BTVzbdym6Vjj@APJo=G)9N+9)FkB->ezXEN5}WPLC?x-xV>xhKx4bYW?{Ur*qxGy7>pp_4#QE44yMHL2&bH+;;4fA7-I;roJwErzVwOeTpvU2 zEU}#ajQXQdz7TJ1WX)Dv$N1^jx8IaHloS`y_vD_eAEW=`H8!jk?O7mod=xWkMw3E0 zOW6)(l`-<>iG!=|k{X$NSzDOy@MfJ|BDp2aqqbg~;-SY2irbSZJE$%WxYEFJhCYRy z+($rq!1Jj%tGNv;3k{Xt=i-1bh_Ip3@A^#Ni%KyLZOxE-G2C_@OpVfg%fAIbhVQKn ze5aA6lw#51Dz;)QWv`0%i(T#$jt?QBjXO-iPeWofUG5_%>bU%EaRZCL_{RSV6Zk0p*0G?6^77}4 z56K3c%GZ~L*Hen1bF*?@rfwa-EQthWw=R$c?B@AjM&Z>8K>M)N%wCPX-a@{4N_sb0Blkejq}(5PC_!SJfc@+ij-W9MSf>=0g*lH1je%p zNhx&2=k$heyB}XBubzV?ws-f5qa7CW%XtWc!n`yn&H=R)y^$W`;jo2A=|$mQ@Z zNh2!nz3&8r06BRog00ZUHuzamoDdIG5FCco9swyzA=-j~=e27+6>X}KS1T*N8$&92qw)4QWch(!T4n4)~{{m=UC@`Lj45GA%kS92X3ZBhO#T z`XD(uPXU=^TBihrN(?IN2;7rusF7dtaUxveR7NM;dg-J=%T$BP&aKy2cAh;_d!k6X zP{}l>s5K-|;%R$fQIs210grCtT75#8PASv_841=oG84&&Ibl-M{gPwiez*m6{p&)x zqT29%rnukeG3sZm;%B-etDPmCl=d^t!*Y&PPq)PZb$zEEqDdYh>eFQv$RW6=1XMt2 z{K;%){LS!9xKU5hyIxUrsQW|TK2&X}VkLxj8IpKJC;Ly|wtosk^b*-0@eW4Ao)tu- z5qj=OiZf8pd-*U9BF{9ggcOen!F7l+aIC3ikuiX;wG(pPks8il^jlL4p-bg&Zl24> zxpt{GFlo*-9@ic`<&pU>qR&f;71P#TMS|3=2oA*xh_)iav1BrGdZN0_?2of&LO;{_ zIIh@m3H?6+vOrD0F@yw-w$bY%AazhQW9Qr8&_WWQ2}RdPB+(7(oK`!vD~tRUtOS-A z??%nwFhix9f3I}19AAH!oXcv6PS_fwx6)G5TV_!`?2=av0-HBjPd4IzCR`+6^MjE zRJ@{-Hfj)}FIg1o6$}UOW2n4D*99bo=mTJagP17T?#q6G@Yl?4iu1XV%$ zSt?Yb!n|l!V~6QYpil^PaI0X6A}3g_IJnfV6fWv6UTO#67f#*Hj|a*S6*yvy=5LDk zdv;XY&yoQ|;w%K<3X-B3$wR5_H*e~!N^r`6I(SYNMvT+OENmHpK}zr}fTrO~08D9_ zh5-izj|NH|SOKRLUA#Ox%d}PKcrw$=oqTA^-`hT4iaCwk{_nvN5VbyG((nigV!|;~q}lUl z&+vo4@e`-MM10Q=|D{tm0srXr;iBL}@ud&`!(bse2|6$71i&rU-vGY-Mc_9-|1gwV zs1Ak0LsL1;EcFhsWB@2NRV&L0jYZ5O7f`bLS1z@52~(*arZ83T*F{08<`ZtVtW*vY z5L|3mu_E+Ou_>x)4@?TEae<6z-i5P$yQO7qr(Cav+-lMRoT;9QG&rBBwv6SqXBxyQ z8jQviCJWK3f5d-1@;vj@3aGqIGT^j;{ zm~f*4c#V|?2#$gZXtiFQ`tl4FZm<4M5WH&`qB`HgL!lqJ0l%@r`&QqLP8loavsuDj z`rx@3_bi|`Y{Nh*e8wHW{gqlEpx)tJCZH}bGIYkcx{4%7HR|b-Rw&i3=?iDM5;QXa zM;t6j)oPxa1_S}G7;{_{aAT^`>g`cu63 z=sKWl8Lw`EsVw3Tg~t+&^B6Ep>2o$cJ2Lgn>2B^D_thhtQcs)*Hj&lv0wS7%(pr(<~i3PX)W(s57~PoaPcD2LAn@dAV4?CC!%cpnDpRR0acP?1Q8 zJl5^IP~3s(rcOpu9pQeb)cxy$9V%G8e`)8`Byx1C&=k+0R50gk=+LkiFp6$EN?W%j zP^5+X>~TfZ573%F-)XS9p-PB(qX2Z?({M=aCKYOx{n!36Qh{d8J5$N#vocIUWs%SQySbEc(fz zSJ&mdg~z*NU{yVvYdi6?eK!5@@G#2|fYbTLH)GO#AHG<;zfvzqGnC^vtE)6jA>@XN zk%6i?q&?Lp!$B^qe3k>Z+ZEdcteehzuHx6teKqG=OtGI(1@n>WtQ{Rgcg!jKe)nf` zN(`^6gZT;%+W!L~s*~DXln!aaZN++~5 z(b^LC!_w^*U(LccPMvpYHt18~z`i=?OB-FwsT(@wO52|}6!fz=pC0G_%s*Wi83GVQ zIp^qhn94r7EYvj@%2XPZy+{hea~c2)N#9Ny^kJcjdP<5WP?2EeqdFnm1U5d#OCRw2 zdJ%Nb?~d7~W3GITlk4UDFth|6YjU1ck?*fiY3RZcoT&!K;6Pf{u+^1 ziGJLj3<1j`1<#2a5jamr+qyaxYh9YJYyUCfGGH^}qX4025nALn8|;%mGn^;p6RGt; zvG80DO`_i8Fr6ds6p8a51FT;r5fi%}AGKQ1`hF80GP}Fog#fb*JqmU_QM6B|S2wA8 ze1E_3CEE*dZm6<^6M?}H^5v_vmCE!wIJ?+}-_`=rQ{45XV(EuEZ^Ayp*(GfryJhk> zLlZ=Pogc#(bS#{M7(2E=w~UjgK(zMk(xPP+pK13XQ@zDy%o?4&!*C( zoa;L)4{4;b<3LX9Pjn9M7aClkO}fQeW{e%(E)HrJMIx1cdyr9u#+=r zVLOJblY!)Jegp^s5(FjfTWU51kd6e@3&;@l6Ahnj4ABF^1k{&?lT@KL?1%bZC0kM7 zwxM+C9?wVeJl6)6Uhb$%pMs8!!Ibvcd_OUQ&ji#Dj|;a90aJrf(~P@?dJz_30N$Lh zA`Q|UEHp8948cSBHT>59`k%qwo4*5X+oJ1Qi1*M`YDy7v196!ORjacvdwlEmh2J>t z|CqE=7ml8FF#5rwsZXL+h!FD+>7<|R3~?VBthYKP6URKeci%99nNkQ;EJ7i{B0<41 zXXse^(uZ3nxPfb7GvR zxuFG{Za6Y9+(-M><%f@DZ>Ktm40a#)b(0_2a6TBgdvc}L^0m-(=5Qswztk}IdcB7> z_>o-jK%0@)Cj^HmnLTw2#|;IJ(60LmUEg**#&Jq@oB)-6{kQ_^>GR}0lpf35bZ@wD zGBgvFlfE6AABPXn#BO~t8`TT?;_;wZdLdGMie}NG1<`tsBDl<{Nq4zMTX{YTC=Z^m z{AaX#=HKfCpFJW;u+H=K}>v>#c6UZ6XAIWvr)qZk-r#4XBB8aHZ07KKm z!B*<2$gZO_bm2K4AJURYhL)h6E^DrT`uC#_q{$=61q%>urhHHwQ?gGJ|8{}h`tw84 znE|!XmWpfaNupfylf}^Z`?%-qCxr8S?jHPAPY>h;KbRM88QP3qnEJ#Dbl-x1Dd3$b z6;0h2dkW{C?yUNV(I1ql=ia1cM*p)P$5PON=DJD%$IrqQCW~s?F8sdXlB0(2ye&k< z%6rap&8VO2ppCQc-+iz5m_>V!i(Q_RqgjK``Qdy37#Vsn424JiT*1dX$I~0ME(h62 z4rDQOs68gLMn#H=`=*X{u285B2ux0&y-657cTE&sh-Z4uwEx2CL2*%wq(Ky)o~MNf zz#anXrFeWvDen_Th8Ex$gKP>yQzno`iJuHZ-&3G;|L;L-b~>w0?sG|l-Xp3+Tq-1I;c-HD_o(((E2lZAA4PRXn*RK zzxzFLJS=TrBBhr!WCvW^_JeM%8qjln^oG>W`~s z>gh(pImS_LV^e8N(l@!1qnf=fl31hmu*Jj5h)qQ$G7wp_jm~j z$}O(;(R>l<+1C2I(ebAVV6Ry5nIXM(IhqUvq7KmW$v_6Xh zbdTmNecnqT!Xu5eyJwuQZSX;HJ}-meGNCc^Y%vF+{0+`&*v@F&UV#1Fwxl8F3F}Z; zZEoO)!=rl8o;2)u3M30n$qqgDXb!EJA`-MjN?@);QWWeeq5@TbE0MKx zJm`yaOfT>_tg%_#o?Le%ljVDk*T1K|KYg+Od4Q5fAnNK6Ym^h_SGM2>i%7 zhxGE&gh+WL(mttW=@AVulsf&8JVB>gXic5>5xIpT`IJXmx+ zY5XYZA*S<+j#+!1cyY(%kkv&c8Ng7gZ8$R|9;7jj%qhqdh&`AXtSfj`aIR86C!rpS zkslJ%I+4oO8i-6`*3xA4NyOU+_Ltau4l$L7YkNuZ0)AqJoY>e4Bv%wzm~b$WcSOOIC}fHeYC_s`4oh2#xrT}`8YnC4WGW5rtW7KHO>Icx+P)>>u@7!f z4=W-NDM$qnfThJ%cMY!s7Ki724b6hvDo#*qA-$e~iztOG_iL#kx?mI@L=Hdk^M3<> z@u&U@nj1y)MgwJqcOlvmq`^@`3aAHB2Uf-Uy)~$1+`S?+Zwr3xU;LlrW54b@&}40q zVEEon2Npaqs#B8=KUzxU?jKn`t>u%H4gYLBj(edV&(B>E8zKaIN5i| zI<=0dYKaE1c=JBs`qS5N?`zP$1~_zhjTKliI`&W(A}<9%hfseFxPtE(yk5cm(hJD- z8mziwjqejcw|u0)Cr{l*0RNqT=D&85^ZwYs^=D6g>1T?eHEWkhxwOyjXS!6_OuP`A z#Ah`luQTVyZ$m;a3Vv&|$rz^afBVxv9IPj*=-L*otnq673Ud1{xO)e$|8@mMjf?_t zGvnQOby$SV_lYIqOo}NZR27OA*Kc^p>k4oeW^w|rW{(II>p9=Q{nst#nCD^)6OZ4T-vV;1M>p$;Sh4J@6bcx zBFPX%+}{~WNVE-goAKBG@{gao3HaN;Dsh6wz{5#{&ypKzT98Q-e~5aNqN3?}h*_r7 zv7&Dxh1U~0tXhFw-9qREu5Vx9HGT#EcO)_FV;8{JbU9K zd+(b^1d{r%@LUcA>FND-j<8y-;2h)T12<@Y@=xJgDIF(C^;nFpLK}%(Z@k*x_TJK+ z=uD~xKRhmy3~`>7jyni#dO%ZMdZR|?ht<`4SsT%3Kgp_6tbF5oMwB&KwX~U-gAGSH z4~8X_S7J&90lRoSE#%0HLYxAf;FbvDS3u*!IYhuajaR3iXiHEW#n5=5;SPN5A&mkn zf>Xr`QV1wQ2@)XJPPz=e{b(X)|N#_5mo z)3Yht^fU>Ya#u`*9ve;$cAFCW4TP7G+>|V?!vu*#NZFexYG2|JQ^+_{t|`rVbXq0c zh=a0&a0{j=q=yHo<}B36tu2E)MQcjGeW!;aSbCTQ-mT#qK&S;dK???hz;GbAVCJW2 z?vEk$1P9kJzzuG$;=O=IQTbRry<5xf`P!+UWt^hr*h3*=`Mv^{M0_m?lOL9Pxj!;g z-ukIj7|Wk4QUF3AW@uG)0Pu0l(C98(m|LzobuMr)n5QOVn>RH1mp=DvCm&2c{i%=L z+vItfWGFusX;qGMnllU!jlK^D<3X}=dr~N&LZpMiWy3=>G18QQZAhjP(eg!S_Qbez z3f@$bIM9a$g2r4y1PLW-D)#~nc2>}%>X)G>EXt?$PPqCsQ5R30F1fNF&byCusg{|h zK17m?$|HB)opq16NHWxa&+1U%`O)?K#dd~$;oM+lp6X+bZuurN{)QndPI|p@hN&0Y zs)->GK*A6RR1gylGPWhLJ5?zCuJ1+8x=sqExa#HPMhqRH>36EeW+Fheq@tjtom!|{ zI#J4jVQ@vQpHh*WWYUl@M}nr_yQI65ON;H&je^poWtO-D|0j~Ma#&b zAV;5AZJIoG$FT>}oNI^xMhJ}$j%puY7mDAPYs^H56rFSsFxuu0LM;Rgr(LBnirJ`@ zFeiVQ4|o^~Z{s%ySdu2kh7`D{99<;-sD1o6QETX?T*A9$SML8kO@@N|`Ec1*ep0jO zC)#aovAKe3I~m|J)kew|#vypHU2^-^`b5JJ7fOZC41DD*@nxND!uu|(u5UNv$ zydr6UuEU2TAyS$R5H8;wzdH9)tH@FdlJ<^SNKDc|gaRC#byb=zVt}XwJyi-*2V%}9 z32w!lB4fz}HyK!bbUo4J@nT;wGZfwe$`*y9>z4hu3x=onyT8uw1*wj#z=JS%BIKxn zI411YcZNe@2`&rkd_}v#rm-6PMAgZ}F*Y50`0rs6)=~BI)k{1zzhBS!=pk?L=L@wB|Typ;ZmGb&bwI7spVfujWt%nJd_q7QI71Y zswb9c$;s`Qm~ZOjkfBPwfRPeWrj)s%vVF<96h4cYh2Ru$%6Oe#2Q@1IeoTb&_L3D= z;U8t~5&9G}?|W}4$8_q?i#G{fAg1z{MNyD$a_<`M8|EfE!oxS6(_yFx;phT@l_EMf zY$Z8cy#I4KY=%X-GN~$+)`NRTA&ROXW16NzHd+C}i^LjT)zNg=h9KA`?O)R2Id&*p zW=LJmnWS-*t8kGIrI7d({Y7Qrs~q;A4glauAP%?!VND(^P28u7#HlWp6lgTK0)-Tx zln;WPgVVd1dq{GL0%C@0Dr#G(5Nn~HlmxGwLYr%PLKDwK>fZz0hCQPc#mG6veYiIt z6Z(BMEn=SrRU+ckq?cHBZ8hQ!7MxPF+{0N^Cfe6J1XoxC0)Z26r1_Pr0_dE_*FSKFzy6J% z!FODL6iyouJ2Y6qDdA>y4N{Kv%mN%cn z4;-rqV^h&xki&^{Kt5`mTe$d7{x5I#+*1JjKANyaRFRVSXa%ke!k2W-07a0s^0wY8BGP~5rECN}>rC~4&P7tEJr4Bs# zT0KRoInHVKNA+-hK!{W)Fa)eXqXNJk+&fLw35A;TnswMYM1G>(o*F1Q;D&Lv0@imO zx}1OdDp-yrMecEq6BO}SlR}sw06O2{=EXC7?D{uW5j`5}Aneq~QwyJ|k8A|k4NIQe zX-nSt7!LuvzmFY8h9=YaA8N%J2w~GCsQqsZPaQ*ZfNuUif2yHSD>O#phAQr&02~7{ zSGQ-*ZtCWf6ckibaG#yNXU|Y zi7nyd2JAxQr=H#P8$Uov3LDiQJB$n&4CjtW5R$=Ue(J16*!MBP-cNp(lp}F>?F^x5 zg5Ibu;2nxz5*Sp~W}@txVh99)G!2*=FjZ*`c5C_mJ*gpD(69;H+?8`tXb;gY!&8Hi zp#!jKwPCf%E(eE%uJ;6>gXbv)u0z9bnGDsQH}tX>Ma&7Y_r5~(G&M(hrh{lXW#27R z-(0os+`oiM@Jv-LTZ$wIsEJ!5OKB6Yf=)c(+(6E^k1%V-x;Ha6P-PuvFjPH^6is0l z-BwM~hgP}!&}!PC{uE(k$Y3}XbmsTbpDBT0KyRAP?$<%W!=3WAEXDn1?#w+HvY( zf3i~8u;XgE5C!*QzaK)s)O1lvW03unMeWGfFlcYB2~#zZuEB6>m}LmSL->k&fzJmD zGz|}nlB;IP#={TBA@3BcRL8u_02rdha*=Z0sDyt2LN1(q2rW@$G(Lsu@uR+?+9tH? zSY)B3*e)N@w1flQ+sY1EvvXmqgX-!9sV(a@LfAvvQ+X4yN z7CLw7c(~k8OOHkq6$Pxuz~je-lcBz>dHDHM=Gmo_V%aVZ%%su9q4wfD78E6NUo1~L zo2|KE?6M-Is8NxeHCdD)q!1-QLhc+u^vX%+@kBvCO$ygp-1v=&_8 zxVe-lML&WpeA+^A1%byg%5enWRIy|Q@8Pz0gQoapu%OkqEww%-AyO5jo@WRM9-e_` zL?BTpdAMkWQ#eqJIotV?>c<3tCegPJ?Rg3n0&_?zZkWUA$dJ+BakzK2MIwr3>V@BSD*RkaQo|CL7WGzuHk4Etu&Oa zSV8bSK_=5__IeTKcA~A@D+o$}cOEK&m0v;7K;XbiA+n5u&51Ur`d)U=LA~g|x~ZA? zv4d+ad>B>+=vc18DXZx251MSc_cTtBn#B2r$BT=63m4}#ofyDp!>A`e8!s)42t6W5 zUuTk#iOt~iUap`lc#hxX#E2Y75On~rq1_6dI0&!dRY1N$d)K1%E!O@P;$Pv)zr;!% z#QS(i1`|cYO64fZuCtUuCnwsetdTlybJQ41)FTCPa+G$f!V{q6;6y;~7Vr440&f7s zjHU^tP?@RUC$(1(({4tMX`xkQ`nz5`r~BNoi`k_q_c@KWjFvnFoDaul5nPdCt!n@s zoWBa(&xK`X!nhkFVhU_*(l4uTz5+J4hsIIlD4ZbP0tE(cy_VJ(gLZ9VnAf`AkFs z6-WoH{T*J>+xY3f_tTK)1b5}4#tC302n{-`iu^k0z6^e^PvLzisv1P*kp^i^4u=Vg z2&6G9b<^PscVEUA*I&U0`F()r;JQM*mR;JIH`h!gDSn|)Fo*V$2K^Vo0qOzqc=y|v zZBn>-`)5zxgd^eyPu&2+p14RdbYIZqUg%+Bpu6m`?|h*;uxP}OcRfxukx~@@0*DrY zCTda~L2h&bmKf-saAE!Oy3_!az% zfAvq`>O}*H!>ijnIM49P*>crCXO4{@MTTbCwiZp9_pz=V6RunXQbpq&96503vHs?p z_?v&_<9PAfzm8XUhHDOqEd>PTRZdJvIf-VmI1l?sQMYL-ne6ECEc=eeq&kcFk~d(o zSj@!t$n8&`-QZ{Ot>&W(&T+qZfA^JBH^Fd17#Z4tDOcFURuu`V2apXBJ!U*QFGLH= zAh4;*a=Zyc7tc7fJx$6=2(A${F|vuF^a3CIp1*^4-}lQvdxKW4(6*W_H0l7oBD~U& ziCR?AsX|E6YRHun{wENUC~h9KCtX98uV<>Q$+ers!xZ&D#KGD`3W9?&;YJw5DL5eL zmwE&;Rf6vwd+<&nlA*5TSgHdL79omRQ*0R&k)Rd5QnySl=Q_bc?R z7OE|}&SzbBDwHc(e2r)^qn;}WG2xN@)KdG`js^{a28 z`vwCmg(M3SDL`E~2ZiJH?^_hg*8+M9sTcAyH&^6s(fVwkn8BJ5O}rO%&A8FaW-y!< zMurZ?;bOf@#;<(YM;A?)#M7i1TnS}BEjrX~LQCEF`IH`DcQ?*V-6X14(Gx$3qHBy} zp-=``(J}CnLXC(!@lfI@_SG`kM=`R*WQ`!e5yKH;h&D&d5KXixwjfUGy$JO>=Mhmn zH0N?&B3fKK?BaA5N`4e;Mfe$GFym?GV5LLVQw>>(f<8SEA)#{Z$vQ=`55sl2_Iv#RqJb~6UWp1%d zz$iz|HWfrZYHi}^wF86CQ|_9?2wcoPMDXfD*gbLHW9a43ihvL)I9n{X={h6}Piiu$ ziA?rfz|?aIC)Zh_Srkbq_erBt4qodm3$#yM@^^_KRZXYFGU{HRD)&ApPCIokr~d9I zZ6xrNcBb?gQu)^OHVgH;D#qL8Xf}?7QYq1+aL3cVbkSPWh@cUIlNQg^;i|pHTBEBm zySb`!^hic6itoQGsn%Kj^e)o}Eyu>=)zAFoNv7_bk)fxA=Qk^S^5Y+Tl*@f1LzB?0 zpJ{Jk9e&>vo(y*|jF1NPEje+9n=cH^t$AVVTlTf8JXVbU}ierE}_ z)>V?RhN6os(I*bkN7Z?1>r2iIO@rr=%DE}Jl5#2{O}d)8eLA{)tr46X| zbCtU?dPmTAWkstHDMyQw_V*zm#JfjbUMcLg@y^ti#(Y&-<5O#vlD23}s#(+L;d+5F z5lT%d1~SU-Myf=@MR~?xMUc;{qEW?_2b#{oYY?o5ePhP$>iH--oZ+Hj+R9SSx!<@$ zGkHDes zRIC?6jm*?p!ZEh>eV&lFVjz&7^a>=aO@Djg4$iM4e|QDbHIyAz;?TkYtqYG#&o-sO zVn8$?8pSO$w1{caGu12fp^OPr(`8>&osX}{4nlCFLR(z6tPK~@@N{|2Lq#TQw1@=vB;@G#676c=Lcj_4>D7_?Q*la2~jDGL*3Y3TpE)@9EG_96eXk zR_K<$?;Dl1Vq_T_LZYxX<`|-24G2Vyv$chFLOn&*dc5a_m|{43U^fhAxzSR8$}Un) zJ?xr|y{B~7*=kzQS2N<~3rq8x~c|%lOZh@MWJW(k-cHQ3X zT{sk6m;0i$yj`0+A+=)08amqticQbS(knC=9tsyuhBgn3v~WNfTN8?yvVSha5UTYm zzZyPsC2yTvfod#6LD33VbAyrnjdZ6#;bHjq@Q8gwhYqZU3iR5PTpj6X0@XG|CQ%2G z7YLDbQFtuLhDE)$Bn@r0W~dAn)#+>!x8MDFE8^wB{-cW@6N99|?!61iBrvd!DF~3t zb(JE|B7fy!m3**TcfW7xKNP~C@pPv#RPcN5K z35lK=e&FWWrQ1iucBn7zK}Oq_W=f@iuCIqfjv*+BI*{Cf&;=KXatI(B5TW9_>4%+< zC-Kb|p{2svc_LM5gD?;zYZCj`1gV;+XJmMjbK=mgmc-Un)5N)ptWQ>*Kxa43@mb>y z>&bii%q6ai#sso*85S2-yqBuk$tjXFVu`{O$~M-r4Yfjj!E}0jXsf7x7YvRyu!8FB zOH#Xv4Tkf^1(TsorrTY%ncEM0?t5vNl3(^oK%N3LNm?t{$&#p)ncFzM$ApST$9s(| zO+&(~B|IAlE|P2k(IA#4Pay4r?MRSA>5;FqvQVj3B_e{3gm!t%wr(?J4d@@BkcQIt zMc14r<8sYKcs;mJDV!eVUkVWKE4#FuubzvkZe2~dCfHTJ6dinDx@Al_l6abX?dktk zJ%7|YV`@zAk)YvotqMg4mJT8w9Vr|%M7U{mfK>u9)geKiBXCtG4XR}$Sp3{VT_TZo15f{8>V{N1hm-B};I`1415> z-3}=Wa133~?kSB@)##MyErBJj!bdEi@ZO{si8)xoY!8S7KBZ)Y z+CaJ%9SKOU;g!&}fN!`=q7aZUQl=UqMaW4MElk`60dCY)DGf!mW#xVhcq$}>qFW@& zc;Y`r=hq-O5EvjA2#D86V+244ASO3tKsycAFeqBz1xrcnXOehFsNBJ<%|TMIB<)K4 zNd$^XB||+hOuw&`X;_H(s&h91AP%{o7!2Xi{t!P3Lo7p+ar|q`XcV5;Jtf;Fx|v;{9hW_a*JuQEg!? zdNKy0QmqiJBI~ZtJ9yMVIpL&6%TA-dMC&<+7$X?7Iz%=}g%EERq#F#UfsvuvmvNw5 zpjt%8TSKr@BD^X6g>kAH#nO>K$*zGo`IEqVf){~8LXtvaI2Eu+7TtzS%^at%Fv)S4 zFZwc)yc%}-!+iD}(i8p@+;ftxM!|LA`c|cQv@{`xKcm1qg~aFMbzrm*acDe&{kNgF zE%>^DCxNm+mEbwl8Vi@9AW{$sym~MQj$I@u^~IH@SJIx+!jx4S)zdvY7e!~JhDli{ zOtwA-bdeXRL=vii7Z0V-wvac7;MGT~K@=R5z^AOvq&ewhr|Z-}Fu6zpqwPwxWUJ0K z!P35kNUIh?+&AV%YnG(Yc*-@^G!irbkw|eQ8-$KqC=`wxsB#oUOYHA*k`%rkD$_S; zyAHSH;k1G0V1j8RTG0RqD}*{Q1XtY(?b-qI3cxFL@DQvZ5(qa*vJ7z`$XW#13I)f0 zRt|k9v8WnV&$ue*b`pp8BGf*Mq*=7r2LRyH*cJPqlJpG6!MBmRG`PME7Ms?0AQgBbkSGKm&IuY8H2MbK z{#plp?~7Qi-Usmx+BCtEliM7KN2ke+%lW!8zgG=}jaGrYfk50u%b7>AZv`?GlV-Au zNg_F0+SG@Bq?FG}U9{K*x1{{aY=5xcNSO_{E#xHdqZh ze;15u9fBE23G6~GE{eXJ;^cC{`Vk3Ha%qYL*BODK5)1dT3w>Sbr^pdl!o^*$TyYI) zB0roXs#5V;=I~h;rhRxQT8YFO9^MBsm{WcQ2s!W-X&TPa-b&^jtCy5$i1 z#;-j-<35E?cfTHQb}J}3bm-tz!5siE5Di4=ngHMpyz##G;rISm8{Dx28bDXqk-QLm z7f#ax@IF}C)@`s@K?K_RpgR*E77bCuCCczVkUS3;U384r2i=njqtif;I2NWQ23#x$ z(m2iwjK+m}X;-cR5zs_E7=f{FTX<320eHC(vUM{aOP& ze}9z;Jim<(Kjat;6Szn+q^h)p;B4Dw{5=%`VOqi_vw9L(^fjv&5A_0arAm6x8Yl!V zh;ds7504-DZ6Clt|BrnSAYp|cx^keE8Pvg=kOGY{xP|-Nnz}Yv&YL6J7hY#&M$ptK zC+!8t3hpI0k#nv(x<|-E zcldkjzXl?OtQ=N(ITOU<8p5a0QirYsaNFV!e%J5Ck9_LChz2VN7@{5&-GwRwC}(1( zkl<4MJ)wW065Xmkv`0%JS&B)xi2K&*Ly@vc2RCT=tj-!#LJ^WBOa@O#>hGd$0*auU zp!m;z={@}6|H&`oZry>%!8I@(XvX;mD_mBLcKlFV+{^S4N&!4gnPlbylL#Kj~pg9!%1rT0Z>B4GV zI}cACh*n5$gRn;+7F;C?L_Esiu#=0=UL;V(3v|;?TR;{0;Kl{3(nLZ|(I^T|r;n3d zl1!IGi{To>pAM_Wq2n98MQ`DUu0DpJl)r*6=;wpgsh(4gsh+Oyj}o8_Zm!;dqgC(g zL_mTbhw^pR_q9@|^qS|jB=Um>OBAmfC?Pm`VkkbRNfae)ov0QK18&&t$cCgkGv|FH z2~Q$pqM|KQ!qsYp?$sLF5tJ3=0OsJ{Om5H=mj-3ry}HHC)$5U*QjB(tn*$Bak7Rq>Nbrp$FQof(6h2YKEnd`@TTW+b;=T7_2Z(dBK(i=Hcp9lME)+`}v z!@^;@Xq^)Xd%V2;Hu(7mK`e1H-=x>PVAvdSG8h@!fPEPElGWNFRimFC`aZHGkOc_h zJ-kdP83qzPgFAjxC;Ok+QLQK%Q(te(T6 zDHf;V=?WSg>YqV+r!4Nol1)3cE)WO>!RKR8wudCx?CbHc)S zTQre_7zx&OXxD3~W2g@)6N1r>^Zg->&7R9FsBSQeDAHL&0Fb9{5dZI|~k(8Gtuk>T2kcgnuE^0dBgpy;> zDHD)@!vvXBt&VrUj;gj)eavJwNzqwI6F2oH_MCY*5~1EPD7vZ;bR6mguk;RIfB7xw z8w&A&I?XPRapN`@)*Np>h)%1@WvEv)#5ogCum9q;4bnW7d85xx@H*p`>*K5J+-(2blWpucO5_-p2*^yLN-6UQnV9Z;|WoUsT-r-f8&|Bo1vy z===)KH^DNbm8Q*5!8+uWFfs(-&`ES2^$=~C0#!3-Hk?-p+f+TgZ{cNyELzQlp^D36 z-+`od-2}PMVPvXqNuE@QG9ju-K(rn8+LGieqqTElfZBl6qwQYdt9S3Aeai*0v5S#X zDtSUJq4Z_eEmo5vWgGVe%G8)$B@*hXiLYvARVE;s$Da;Khf1AeCSq zS+HL>DkM(qH+ZPjq4?}*wNH4iaCGFf1lWB+P{>n#5y2fMJThUE%trio+M}mI01Zd@AjR%1$A|VO)`xs*Z0)l8t1cL`ITS3*y+j0 z&@MREBE}X&TahM`&8yU*KW5W=tQ+>5+io}1L}<&^O#1<4$dwEs+S3RUIpo8Ea{ACj!?Anq~#(qMN7f(21b+z@1mLUC4>#bEFx1 zS;<2u4>N^71Rip*>1UlLy#_;YryWQc&5A+rQ6z35+Rzg8YW%M(DEi_pa1}Gw@E}nRJxeHlUfv>2ul3E3ylSL3bZX~eTO>;ymp|N zGlnDLW^;=o=)jjk&Hj;6|>03?cABniSv(IRBM7V&6rJ5bxesROi>578L` zNOJRxZ+ajKrP~H8+d(LP-EmVorM?fU2(fp0({hb75LkdKFL+HF@GAk`bZDWF<_=d* z!MuV{Nc}*qz;|ottpYD^LslOR;%tF}seW2hj-qAl(ySpnWW<P2w38>Ts9BY*68Zfdhw-#PkriB^4ZV+>ZyC- zB)DiYlt5)Z$e8xaAMoO;z4lY+lMpQn{lUNCppXhN^y%f?pdeK~lOoC~WjPQA@P(7k zRFKVbokcC!9UH-iLi=RhQ39NXEXJgY*H+K)+PgRK&pq@y7>L#@kL#ONm}t6?ky!}v zR}Lb;N+jlf3D$T3Au~6pZVpxtQHg{}0p9^#3$z}%RnQ$nZZ)_bUu(or+_wsb?4D_ z9=`Rs>pZU34cwa1D1)L)S1aq>Ub4hwEj)4-`=!t7css?+%`EMW)-WbO=ng6kzV;qr zb^Qz>@uq@%1ovo{_2`ky-uB4N{mmL#Csy6l-~QWvyBZm~XxJNhYD|lH^OQ2P*HG)T zWV@QmNItr!v6C$oyO{O1YprDIM+N0mL{Ve5Y2?*#*@Kj_AU70X)mZqlCVNrG!vGq> zhv}pEqd)LZ;Im)*Wqj-9ui@J-U*YbiL-(?Urxq)AxI(aCtgl!2vVRBfK7W-PCZHIl zL{Jj2FdSV2Z3kIm7-UaBT!snW4mC$Y$DW4)K>+W zPYBpcKlMwB<_tbTINs~2(hiDSk}A9)Ca0cjZyZytutOb1vPm^u0HmK{)GC=nb znoba`HITmb9iSR&o%IdpvHo5abDw@o`Lw)mAzmTk@Z#e!T?JQp3Z{ngvz=wA&jlJqbT1{4W&AM;-|iV|MZt%;Mo;$b#n!#AT}mq zKn3hVsm$DF0>BI+7n42$P=Rv{mx^HmND^5h4?rMY$Smy1L6iWFKykmo)B)b(BOkfJ z@BEE#;@RsT0BZ~3P~9G}CRDIoah%p0fPpvp4gAP&`rY`E&-~}0>LFOeV-1lG!Tq*E zqYV^{Z==JX`{n-#|JN6P4y&sM9Rr?MxN@Q7$#tvv-d7*RcRu?re1M+ewb$Oj8`sb9 zta%2|j-+JL_HC+eA5tz52oQmjkil8ISobWX!G`)4e$7}5;UDVia6q{duMiSyNyo_~Fm|5Ps-;emh>Z??PfUnWI#~B|L|Xc)ZPuU-;7J z@u&X${{ipNyZETQKq@M$6kLi#F8m%t=1Cb41*wBFgV2ICl+8%bkCKKG86;@QLA?jq z;bsuto*#eO-KCc9k*LT2_}};&_<#TPFW}{EgEe~)uFy1B!GTCFrU;0&-;gTY6Nnj& zbCH?`=|SrWN>^z08ZCYuaLsUNW7cn(cz+F@Uk3qty;#cGriXLO=AfNTpu)ljTVD3$ zWMt^UxUlAs$xCM6OL5=BBnbdf2r>TVXWzx2`zhe=&V?^x&LEYi z>t_Y6O$a*|>9T+sBoOJsoQaWE* zgQqqqpesNdKopLi%x(dhX#6w6RO(0ay-@U@;e&%!d zmp}9O=XzzE!_x^s6NCb)xzw*uZqt)k7CvY8sT*#WeT3yGBK**#`3{ASR zE)8nIeaXy#Ll#Oq1QIJxtn|kHqK*Q4&>7=mr>9FLLrjEN4Dw;_eK3#*&4=>EK5rwa z1`ANCI-aB_UL=&|5P^uqx=zcFIYib6X$l<=m9hDaHrlr1IL38bRgWpp^94 z#r{_Ua3yd`a6qu=5CiObV(clfDpn4-dEW}U^5EAyG%JGR6~GEl069R3;B!4S0#P77 zO^R^S9Izj)P&sBU_H=T&IdmG1$11qH>d?_Ey!-ZhL8nCN_?v{MQTM`(E4~TICe;T4 za1*}w;^R76&=Jt72R&=??W9MaHv_^YT3KB>;B0Bd*Z%Re6|Di=i*fmVI358k1wozL|9v)#E z=if#1vc)rIbkxC9N+cNtE>#|)+;mFiEEoh=M9<`t7bCcS$%kzn!Eq>s;JruF3E&ul zn0JVPTlQ$32TLfF8tQ)uT#UXGa!@$5aG_2YLbjM$w@e*i7Ogf!0#*&iH`jr|!7wC2 zqXJq*8%IzVWjkEdUggm`>3YEMInuEFw(|!s&(?4^Z^CJ?Mga|U z1+-lUQAI}`I`!c0E|4ms=$h?|@DBk2XguILf&+u;8qPIn8U}HgB!gv(z~oDy0f+vP zWU0(YQ>Zqf9ElSs$4uJlszjiMA-wm2l=PxVkbly`xu&P2T=;l|^9nARoL(#$3Yc0^ z(U!jpiylK~_DE4d6!Z$@UdHcXp^%_ZD41Wo7kJ+Vmmde4N+SxVf;D-t5*n|N7;f(=2+Jv3YZRs!E`%mNFQf#aBC954fP#h5 z699E+1b`QaHc5$-iQ4=VRac5j_aNpx%D;1rKw~uV8bk$j<8XcDAnd>hS{i^L4xkm# z&}c#W%%8tq`ri6rS~P|AX}|Wgqtvo_3)&kS9gZzs&|hT<+vMf_7D!k*Pp}-o{5i zJu`#T%XxvgP%@;Vz}JaL)oZIno+*k&A_nPS!d?I<4A(mKY}QdMf!!zP3Te=a5Q{26 zt1AE>Idw0b1H1&mv}b_O#Z@mu1Xv4jD-N;_!dkBK&#)pF1l@+-`kBItU*lv7jb#Jw z7##s{nkQn81eS=SwFmh=tM-qa;@;APMwCB6A}Gna)#3&!E!JHJw1n4hJzlz7cyhS$ zDDJQ7_*9!RdiSV7p1R->&!y8tZ3?Ctw{cJ7}&z0KV;CKmc^nWz`#eD?+4% zto_&+qMfZm!6LBofVee4f%G-Za2y21#8qGB>gWS`ZSPzzTkBXV<6`o_#0i>}(LvC( z4Ma*+t(016a%!&KM@hF;A6#8^eF}BK(knvXt^=;FAdcZUxZ)fKQ8AIgDd5~qh~8s` zwhr8^;M_H?^cjSGRNV-!KwxU1+M?q-+&K-JA|MLv>)zSy+1Mni)>Jk`6rLF@S73J? za{`6jRUiWAc=(j{y$Z8Wb=na0b+MPjXH^`1FP~KP{2Bt(_t$Fa62i^b70uJ@!6WAt>$>1{#h6#o!{E zn{-BN2lDY@r>LvozWG{%Tjlrt%YW|Qh zn;-Zuzw@nceYP?^qu2qJKuLeNrC4tRzIrT zK(%N!SuWyQEQqRA^MvAkj9}9^Xjc5-SRil?KNk|PSBMt~dj#?+F-mTn;4fR_a>3cn zZ7bQg!#!gVu9_9pM;DcAl9e@B9g-%i(r4-D?Dg@>ca+1tkuzbZ_ykp;VTV=);=3sN zO)j;W$709Ap;8H5C1(HT5PKhbouf940LP))Jv#=o4|$^;gPR8A9XJ+_V-AZV)(*}w z$OU_}Yv$bRn+4R1t|Q(Nj`t?q3l3tT>J-FL3>`@UDh<%E0-J8Uiez?1R?{>X@ zaj}Htl(2umnp}g`VN57X(K^WF7jvkXY&;W^bq}I93`@M2f148|66)?PH?gg36{aicxu?rWvsxSdNl_ zTq7ApC9XY=fI4+)DAt)8-*>;3e$$EoY4u&QVvNnAWf7q`s@UnsP`Lu+Hdug?r53?Z zR1cuJmS4I$qAcXEQ5Vonnkteo<>nGiQ4r4%bl%VSybTWlBSWVEfs{QHbhLUZT7cqv z@99OVgG{*u`acl|S*kP=isr0BIjVs)4=5=#ouaixv!Hr-r3alg&WK^+F15CXUApV| zo%lwX%cZJE7R1Z3NLY_Bq$^C?>0?@la+FGzdT73K0+*Awe0YMX6A3vINA^CP?=~e@X6}kdp6QplOD# zv*;;8fvMEu8J~SLKmi7J`^nyzG0~MXuxQd;JijRKq!4dF6R5u7;2x* z2|?x>PB0{(af;0BN6^b6s~iVJ3O|+MeHZLs6!ILW1vD$6-+ac@Js%5Xr--w_uL13X zcwAR4M4rL50MQCq0}d22BF6`XP+4ty$F40Cn?W((%(cJ58iZBMDdikN;n8q)jZ~}- z`5ZKT_EOG4a<3;hWkoMS7IRi8dqDjtl5>f%VqSo(XuzS6ifAn9l4x~oE&0#~#9&9U zWdc?~Cjz-rcnTra&N*-slS&fM5N9tHH!dWdq-edIP+)C)t9v!z28D!^D?a?-{I9s$ zOISR`g1N~~DEVC&2vrh!%URlo7RU1$hX6q72GI9HZ>kG#8x?2QQn^CzifFJ zio%1T#6-}GjpjNvj0~L=hbTKYS&EKmKEL-a7>Hms)eqm6CHVNr!7)iAUp0WRYYvX_ znm9W)s@q=w{U8@tSsVu&%(Q)M!8x!Yd4lPQrF)0h4KA{}jZ0o})aoQR zN<8`S*Bq^~ds-4ZU0wEUR4Y(vv=-u_)hzsV8QKz=8eoNIL83 z!N|}F@RL9JlOz%X!~25GecuDltBuj>WjLT}U)XqRgq0&@3sKTF=WnAXBh{+f68HeacfN)Oll#DMyE(E29CNCjpm3{V+*QiVuHn?D=>$5 zZ+JGQR{Xs_CWK7h8f$hb#eYE`q6(-KL3h&egwz8A!jXMU1Ixs$^(3fD9Xm*?IpesC zf@#oLW}1lGX1B|b+zq}ZK)es`2UqTOa)azr!)wzs6#6f?;zT+y8c+}gA-j1gIwkXG zh#XTvLSSw{o&idlL4=%31mrB5wf}5Drstl8;l0@yVh;^@l8AGgYa#pCAwS{rtG)BR zt*4D0yNcEC8=b`VQ+y&9l891| z9U)|nR>Z=vG+2}-oyp=vhIcHNO zKZ)k08g)QSJ{fAKgydF3%VCv~d+N-Uq~v>)kdb_sR2}6Z-Adp2eV^gpb%qI_w|r>l)&W zHOe8OvYb-{?7Y`;G{ zVvyCkoR9kF?h&E>`#5$CPyIY&3^JDGW2?*8BB_ zs$^ORCMJ#W^FcH!i~C?W`lnU43#NNGkO+XTTn6n=jIy9J)eTdCcnzWs&?_j`P)N>w zq*2^T6;TVNL!|1}x-E6|60((|7+b=%K|IJeAXRuD^3OmBv1Ub}L{!-WG#z_OL{D@# zW|s_ti|qFH7L8j4>qDqE8nS&0gE~RG@}Z!Q8YS34V85*WQ`b>%myMPH zlJH-_F~vd|o5I!l=uWDi`Jr`s;-)=XpFX_Bq(*738GtoJqX zlHMMt?uSc-S%yvkkrSIjAHgDRj0p?PG5TQ;y_d*MITKI(s|BxBQki^EV7`;buFIq; z0X$OK%cJ)mjXZQyTTQn7oXGPKy=kOscu1B|2Dn)|bI`j`Pn5|Dq!ZuGpeXjsw!QYu zxw-EVl|?i4VcnX|!uzL(;q!60fvESyVx&Jh<9F?;!^qGH@B=^a0|)I-M6)ZBAOxQO z2b|&iR<}{K(vXyE;wrHlu>YW>vH!{#u4r5X^%_OS0x!r(^pfCLQl{|uclzFoz>9_& zYa;QV!gm7irTQ#oHLQ+-@VWPZkGdVvu2z<0E7oD%t7zK}aKS3VsV7L~~pLbY7@KXD!YVY_uBk`(?B_-x4 zDr_ie%?2msl)*YQibh1JY#4;i6JPC#dOQFCkuKNME2Zf)Yxl+{-mMW+COvR4>@ljR|J700)!x( z((Lz%V=tR>X%zrOx6(KSA zfrSWVlmG^s7ET1S44nYK^|$`k6Sv@z3{F)ui4gPt>>n{R{kfL#?x?rw2}ZMp2>pLZ zM5q#*4!1*pAMZUn-@w5_A*1q8>@As<)` zR0dp9fEY|3fQutCSGt;dl{$TGIjShSM-&vKLc9X7#>zG5RB+8voGOA&TM!2kHw`qQ zI#?Npuw1N(j*pq1!1@ zpF|;TsDQ08gx`hz)q|DMNrxBm5-Vohtr!jtOjlqIQBRHmR)LU$1-a}4Dyzx7H6gZb zl<8ZuHAoeqFW5tgvs-JEB1UE8_v-fr6$ML5(pf)0qy&is+NpyuK+=Kus$>$^Wx)jV z*BQzvD1vJhE@tjLV&0bQe{VAglx|m&7?x1AdNtv+ErH5VDYNO z(^L6xHi2b$KZC{5DT7I&>cMCLLadut2WJ#9bzfIEJl@j@5l<-_0!d715tfak1+J(8X#*h#pzS3j2rJ?$C4UJE z%iE+xm#J(=5k_*)Mi9sm#T_~j)@a-s?)n*Cya)$(a)mU>(qNKNN+I}!F7iUIR`0o( zrb-O$cA+Iri*LUAD%=g@%B|2)ldmBWlT^cX*x%=Fv@h{>b?Z+-_-kr6mK{%TKgei& zTwlyAdLi_cc5Zu{=OE}y)gQxx>r@yDsI9Ng1xAKWgPq@i{Z}IAjXGjmwG7U?4HLT$ zDhdISK$LLv%z^1`s5WpZc^^X9d9uD_OP(U;KpF_Tk#tBQELYV7-hrYuiy&}OkJR}! zNLn=7g0#hJE8v~4y?~iU+XjLXd)k5azYa4eyz8q@5++x zg2>-NL~|Jz-LP%;qYj?kl5za_TAN{djy@66QplHv5t9A(O!r1jhztqy8NvIXGx4Ip z1jGHo$k2&k0_yFkgIeY>3~%0&_oMn+It5)_<9k1Kga6(i`7Ss#U@6BK7AY+eBB_97 zf4b(%t;Gz0T2RbW6kK}_fQKU&vOKGT_zuh;!1w=#?|`FKD1NCSztXUNH7BbX6f7M+ z|Lrg1=Rg08c;~&h@nZcRUcGu3Z#;jFw_bk}Z@uv*uCJbfS@E^**YMd_pGEVg3u%Tz z*jOpO0Kg-WExiW|MafmO3ug_kI+~nNrtkF7m4=vCNwJxp9D-t+BM?*)0VK?s)q0wu zoK~UG(1)-W^qW?N*KMeWwjJ9D(Ud#r5((5eNTgSB#c&cB88Vy=vf5^t+hca?;ov&d z5zRy4z>XmVeDXtA_~(E0dmyq7`XuJjp|Z?G(xgS%N0q|Evvxy6lvp54j1spM?ME8w zfss31fWPo}e**NT2VWDw9#`L{TP<2!(CldF zmAna=jWmCru6aP+6g|?hDX{usohT`>;*gH*(qVH8lE>l>>f0Re=lGAMFD?s4hE4>5 z?(|BGGf>Zezx`mRzWQNvN)*npaUu7R1WQUij^q%W{RS3-0vyGp{2Wk#-E}!$G5qxR z<8cR4Eb7`9+BOs29NHVInx`p;pskSz zCWdRf2HrV%0>BL#T%%EfP&gkG$922_D)8MJtf`DeKN^cbp@!Phz8yS^$$3cVK&${s z&+RCb`Xzpl5*BPAVp@rb(@L9&GYQdfV30cSs>6y1oi-rt0@>XG^8#uGd4f8Iq64Wb zbv?a8S%TnQDY#{0MrV#hSd_r*0_jx`u!oRjG8J@R@xEp|VYqG~oyz2J$fEf<5lCR$m!9{iaZx;N*pbtyK7XWrM3QDcHfP)%pMyN zny_-!S5ZYvO^+Lg4!}uZJ0=U^uWf^1*e_ePmUMldG=^>xRBfsHCz32e5a*_-v74(a ztQ>(^0cI#18VZ7K-u&EbtJ$^bnN4B(iX@B<*`Yx(1;D6i z#`X!nm(hBd1TYbXq_K*b%^Ap6A)3)5Exq>s^+k}0yJBn`5C~5qEiO|w~VO1iaSXSiT!PJlDtnOL3wpw64 zgSjxssZH-&nzJ|R#xUyKPRCngjs?_b)dd%dOO*1T1qjF0aRv-UkJD#(EOw*a=HNrG z>fCx|Y89H|j>7Cq2pXBhnd&O(nvap(9R$_d(UIe*w?+^hJW$t!r5B#PyPGUWYK^{_ zvYiC0T939Kd)A@RFTF2m?%SRlRtpizoFhdl7ERRrJvyShMF;m}5PrY)+>3qOyK0Lb z0-F|E_dGr%>+HFh$u1CP89EVY)8wLRO@{yCl`Ds-M@% zQ1{Vi)g(@#eNGkPThOYBrNh!abfpU`pIU912$wJx=VX_0Wh6>Rl}IRrJv>*=j>;ml zBwB8hfI0}KgA0k_pdCPs1BBq3CKFbGvVb}?kN_rh)WK7W6*@pP64#J>TG2tnT|<-e zK-F%W)V}>JN5zn%aPcmPba1@NiBAiuF7KVBcT9bHGV@Itxp-$@qGXYzGs;htQ!%Im z&{ds;YP{J#dQLqrkGf1mGHN|^bUY` zcG~At-}f8)Jy;Q#jb58-d&OY;T915hpv|8vRS{vyYF7()E2~}Bv)o5$b~>hy8O}!; zt|A>SIwI};KE7h={de5nB04`ei>%>NVPxn;*fq5N7=1Ox zgb39((ob2WX&973gb)eJ%pI!7HcdrZ=gz2Q5-gudm=t5_j3r}ba&ro6$e;5rM`?4F`=Qm zV3Ye`a|gWr?U#7xy*6f^F4_kso)^f{;13c&Bu`XXYZE5~BE_nSsT7Ie9DyBhwOXNZ zO|GO)+e%@i;iE(Crt4e7`Z}S5BC$^N?9~Bz+hoW`J7@2LXQK0zKpKd7jvk{#nkYZcRB4Tt zNU8U2F=?pC9++^6ghM2CStpdE5bM>zxHU@Sc1pH7B3;}=q)j>u5B%KnIrqgAhmoNZ zVW($4t)Ak_a>{!%mE$5=JhHi3V=}d~df{dwII=u~q3R(*0JVUuqR253m3n2lO+a1x zg;G{vBK+(B$G?I<^%wpw{OSg-xdAA;kW#HEVuWnF9K)w$N5=9CjvWXHS(@WOA6x?h z(hi|WYX|jfun<;y3wAHy_$6L<9X|3J@Wa3JH{g%`xBih(wVqQB9-t<2N)&Xrx&i_C z!O#34e#d8iaO|>+G$%zPwZ6j_-}@r|xxe}+@#^XYuJ8<<3g8LX{0ySkU~ync`I6GL zVbpJP7-vYQVqszmZlJvjkEXWNg7WPkgu<7;>xZgua!O8Rdf!9>Q|@$T1~~_>67vle zQB$yqB%N4vd3i?b8CSf9*B8+CJD~0r1PlQ|Nzkh5T5l|0Np7BoHt@k7;Q7 zsfglmEm_{@qUxqA18hEHe_J|lV)(>y9#5fRGkWjXZUV~3Dxi)Yn}t`mL_j@6B8miX z1En|c-W>z&P0Sa>U>E9mB_b3CeivK>l*m%b)x%kf1mOSQ-kbc`vu$~O-?etc$;{vH z-us$rs0(F_S3nsMg90HCU?f2H2wCy~foP->0)!7WF{cdjA#S{1Rh}| z1Eo?G$hZckNfhevNfeeGDEJ}8lm`UBX$2C0x^p=-IsCTN z%N^Lc8C|gDx!rr0g9$?`am;OXa75IBtHgEKcTQ;{*%O(wR9lcY)9Ca{c=hTPw*Crk z^8vs$2sJ=etU|9~7aWWgU8VOh!zN)!V?smi32~IaE3P*w90;lmQNlI1sPGy;_|Ct> zyLSz~bbSN3h37&=Ch1-;6d~xu5LLYULhy~B`vC7ZU&2RUy~D>p(cmjT?7^FB)L^J* zpbM}*_$uxzsM@Lk2i61|p4HT~938jk6pzj!J~~fxGGfj64DCuAFsUID%`!OU<9Fk= zTkLlNQSh4~C{<|nJ=)EGgx~|9c?IY_h!lyIXysE7-2-w3;2uH_Kn=wPh}yO=-qWeJVNsz3z{Rul5Ts(jq1QbtI15za0!rPY zZWynQ92qBNR*jg6F-s4Si`j=9Dk+mPqM3t& ze}+(uu^~c>8m@Nu{EN@=YkuS_(6COQIGe(^XJ?0WD*I6O0=U0#@#fnmU+La zVTk!KQbDu%GVcd9R0;giH}MDl_!Zo{@8I^+7VW2s_n#~7zo_x{yMlM0dc6D0qkTts zrN0$*%K$gf6F$00fyxu=oBtnQ{_#7!{xu$T#i(2jrcD$o8LD;Y&*)DS{)T(Odj;Si z9x(!qW5+W@Li8fELK98NED9Fj>i!14eGTBhIobIf-4A=WIK`r}e3qvpN_4g-I(>?C zV<{#Kt;kb3P>-wv&*6TfU0>({pT}+(2}G2qFd{+44$3v0i<%*Js5dT0T;+bt!cb}; z=bd*Y3(_$ik~alM0fl2#>~IMj#1kN|QSmE$_W2K>^j}4v3_M_fgmxwq9g$+|75BFe z-Zx-ZLH&n#`~3~>KD$9hTd=EQyL2MY%)>dAZZZj1`Ta28(6HY$d$z>4^aui9C$T4? zcd!44D##0z8T7gVUw?s*e%N6H03u+&#T9B)Z3TbJkWYUUuYU9{n4E}D*P#fJVYgL_ zkADZ@H~hp`A=lqSjZN@<5&@CVT2ke(I0zcaq~-C^OtwXcIY>fZ5QdoQU^0WiNAvBq z+n`dyHEr>w>SO$kzi`3}a!PdY;-ntm!+kAt4lo`E6NXmeAr92}<2^!6cAzGTx0xPm zpLGj+r9H zMh=kxxTh8mS)f&;w~=}Xd2H_#@A(6K@kXH1f~qPwoNBq>IiH0YhjBuTLJGC5w)X;R z!%E+K4UXN)aBn|^zIlu5uP_8IIDKb7scwHXR7ELvYI=gNtOn(P0$MZl_Q*`hDPK_% z>_;`QQe+kiZK47qfOu4>P@_hbgAbBjt4pP@s}SwneHN^cLD&`YsM!J9LPX$PR~)6x zTSX!BWj1HJ`zt7E$WMfj_CQI&4tVbyDDKeI3iqmlyQ+3*rsmta>pV!qv!RBAUcbX9 z?LDeH0oN^@GKdw99hicNCyfIo1*pyDGD_(Siqrrx&3IfSL_a%bkhZv$7Bv&<`X24; z&j5UU)Yv#BP8AK1#pU4e9Zg#)U_-U{Ksj8I(|;Zm$q2yY%KV_l6Jx^AbKrpv)FT(Z zXP_9$?OptIc*rw^;HnL%+JK06OnxJm=0&X<8}Z6dLndG6USoSNVCQ0H=u^YNw4$5f z=YkrlK`|&Ko@+Pgy^w|s6n7QQTI~s%68FvNr3OZTXMi~r>ZDgjruDAyePK5 z1vgtXQPAk?4o)QCD%0v3!5LOa;pi1^@1)4o zELb*)E;x50w4xBy0Iq^B6E*-8&0P)n8`RZR)RZjm$LUbwi8eoUzRt%KiNeL6ZB=d9xs`zF=LkUH zK~$p^irI?~K8@Y|Nudsz!XYPFjOW&PK&6f~&6byXnA1=09E-<}yJTh>mQRasg7uO&z5{P4>-!$ftpfA{IY_u@#L5=v=UDi?mqR5JX^)kR8*Kwd|!Sywe4f&|!FNLyGN#N45{N-s$5kUY)=No~NzZeFnJ}8+-cvlV(D&5`)cX@ddmB*>?tW~ZUqsy@BKDqpDT^nJo<(KaE#VU zN}$m>jb0yj9(klEp$9?O>VzLefwF&0&7Yj5bVL2c=f zn0G0anP|7ik2*>d=!A>9c81IT8eU@FCxs&ciHA$huzSyDVY5`@-YO_)Yv;#vH^}#Okl9B1czzuCj2sFpxd^~SZzE9Cx(}c z2}5VdK5fvx%+g(`_v{|lKE;{#^fh9>ZL{x=$iuW*U0?MzXsVqSCzR7D9hZxTdpBpr zM|rU_Pb=Z5iJ)OrN)7>vo)U#0QF^VQn7A&s^??&!Kj0`-NLQ@@)LoCO2ZRCdQCD@2 zh04ARR0uN1lC*1j{C>J&Uaq3|PaT@-q0#uB8agvCZ%g4!9WSduA>xC7Qan};pekFO zKG)8?mK?l$esbLT7%3~52||k2Hz102z4ACc&flgxkKBrA25Y0x?4i{5%fhAH1}rG@ z927R^@_ymqBa|;x@)b&E+N5RDeinx_rYpKU?#J-`1MU>+|HpWGOc+{;le;gZ{ZhF) zo94Wh9)ndsbr0z*G|=gMtcD^+nov6rhv8}$`4{tE8dUg;gmdTwqNH{w^j6zR2vTGx zS|6+4pE@UI(k+AusVW-J@GXJag$cKqq8-ZiKJ3^bVoHzBqfVt`>x6?zX;utVJT6P2Zt$`c{OIIQ-DG0vhwO`2)YT>kp1SL| z$M~-G&TO2x@nER%Lj{7INm|2eoG+>oQksPax`$=#P{3nF^r} z&+`;_Mbd1R$16+DBn?*5YL&v=)c(qGR$h;!sRK$!?ek#Wc4)W>&!sai4ikn}A}@9) zhW=RfDueISkB2aJJ{a?J?VRqnPH&MKnH*Ddb!(@PmZ2m$0qj_SIaGR32n)FN@Z!O~ zLJI=NkmA-0T*@>=QOhuNpxzPL)BU%b$TU{x4%NH?^&SucNf1ifrU8insP@d=<#K#_ z@+EXN(u5eQ6_l?){t9^-W)E#@Y}lcyDf@Uhok&<%NN1x2(AZ(JjekW6B*#tK9&NvS zlQVDpHhsbIzEJhqK|OrWXj?#4K&;U;y1Xr8=kNrBLh3+NTtTbgR6NpvW$7mwM5%il z&rOr9DBtc>$QnV26}<(p1)-j~;!DPVNCUudu{quIg{cAa7QhCeCOV)8Qx`L5Em1Id zN!Fy~H9*-vX}qY5S*Hm~!HQts1%)wyOnzisn_Clju0+d)qhp`f(*4O z-8qOVTHm7e9&PKEhF8kMU?>-d4LY|w_o`kb&o zB5n4!Lk-$UgA&dy$q*W^trw5RH_$efC3*aDpA)u z4!IVCwq-x2AmpUz!QOFtK5V6BS!Abr4s=}tO!g*>p4NkS9pFd_9K@$MzkxuYUsTs=Wmz<2dKBw5n3Pr1eMjCkW z@J)-R*&^}Bk}#BVGzX)PqV*o$d$iJ`X(iU&I-FgCQ^*G-U=VIL*Knxdl;C3;>QWmv z3=S;CA)%ODG51*fHcH!3E)V5gRJ%=8v$AqDEc*IdZ zWuECrpU{C5+EdXN-G+nf-V< ztm|_@q0Ye8Im|{wn@aFmLGVlPak5qcLuWgXIa^0p1-|F&LY=m355X+xAx#Uo!^fU)o1%9WLMb|rdqu>l5IkG&(fS6h zZ{fY-;gt@?Wm_e&>Fa;xCtw}UewYQ zy+ul=V;-hHvZ1;RK zVt=p-T6OSfaC`4@E8DOtmKvZ`Gc1L_Yd^XyuMa7Q#ZMcxk{}c>o`lT3sx3%7pe@ul zAZ|d-=Xle81W~{#LD7P!?(dBRP=QlGNI|F|{ysQ@^Cmt}C5Ds>M79uV!LC9JhpWw( zu&oqyzk#|24jVLRvAHHxXt7m~Yw~c|fHjzP!j(xmVnXnJ%BVa!trMaKA!-{WR46BZ z1A4>2_2B}^7ubeYXenTZLSQ1n*Ge@VQ^5qZXd&9dHx2l0i}uYe-uheoFkV49PUKuh zMfLF-OtjQ6P%}J94o?Wm6pfDl1ZoL4RgEjDf>2p=Hm}lznOn3Ibb2EvnP7*{0}(LQ zkmM9q7_GSUVJm&BqM2^$Ru985IrGPTDgln8rc?6siBcBrWW91ADpLI63igSzw8nXMIIa`@FB3jS;Qcevd;G+O85CH;wJ zvrOUako|ggw5yCF5=G2>2>2Gz281nC?g2Eop*vLfzb-_xVcM^SG~{h*nBY{QbPvxB zRDf1mNHl>}l>^{Hq>l3tBHT6%C16uk5bq$YaL$31QC&IQ;SFBvD-bzws*a9ezG*pV zVS+-2LNdJ_!!=JJvAr9r@RjXr{Lr^P!uucj{NNGtBmeYm?nb8yH#A=|p{1w=qmTfx z^&Wl;+}{fB-wCd+9A3K{c=cd#p?RCWevS#PS4b_fz$oC8b972LC?0pR#cg$mU%LNK zc(=KS=OTM^)LzY+s|&@+tdeaw?neLiM35dpSSt*jrDwN1)d5e^_UFP#e|uS{f7X~V zv;y9zaNu%fy2HO`u(}*il*?9DDGkb5-GDhai^NRR@s!hf7ukB}Ip*Q);J_fh#sB;# ze>Z;mkNtK~(?ERc?l8%OaQ-e!b(+0oBp_k*q$4e-u_B^NyW*5gfUpNMFfB`XTN|i*B|4my~YRf3N>#)%3vn6XaE7Ost<6zd5s_Y=tuEk z^#M?E2)Q1db1v=)fe^t1_@DmhpT(bQ{{$*kICa@vM^JFC{nj~|iw1#V9|JXirig~l-)U!%q~$nBWylOJDHef(el@}J><{iQ#Dicx+N^lM0}w`_i$ zHjy5%&*Z~~vgQ`;FxgC*rBi=$@w>uUru}VmPy&*9=sYw-$7Z?z`ZvCHswP|m`1(_v z>rZ~|$IfXMI15Y|T8YCPsHev50?h>Q3lxxy>w z@Zl>5#dXY?IDDX&!e|jiHgYi-r0Pd8amP=HJW}qP&QVhVR6!rp6TJEe5u6|h59;wx ze)fC#hd+OX=1!q}6Q)HXRMiH|jEV`49YlQ)Ix2?^7~&Wt3{l40TJW#l|3}{e_Z6X0A9X7Np%6VJ8;@2L_xHDWw`p;6&3Mc2 z0aAD$_BVG`Lz|M{hyd!sJ~{XjfkWFQX$M`JkYch+Q0;wHP+UzE=3ogL2(E)9z~F<1 zfx+FK;O_43?oNQggIj zrmJ8=uS~#QZLA&zoB?x1xJBcIZsnBGgcB4%5Vc@Baw1}2NM$x+v}X|zbbHxU#R5HJ zlIDJ$KC2iXRjF6{toz%!OJul`d9v&47NkLx2JQ0i;YDW+_d! z(`TL!>0VZl2Q!#gU>5s~!C_6ilo%iSxAuvsl~oFF@sb6c+T$i zy!s9csK>3ZESD^8^Ki}`cFvXik8H3<3+rw0A*ssregDGGvvz2}*rjAazFU#@RSj)5 z%@+0VN|?aD@)E0pAx=Jsg!(e(c)WWdfdTboYC*119Y)R1@QdPJ>pGsV)PRf08B-1~ z0~AGEjr-b85t{=Lz*X-y?kYi3r;4%BB||UQCwvtkQTYfDqKfO;#W($ScFCh0`9Ly( zB~xNt@N*^mZ?Cvl$xP+K(Dli%bxpqp)mx1!rf5s?o2#E|C7!_;0emwSGc_VMvQsCV zwork6E=&&mkAYL4G@ywuaSPqM``z7IO}}HvLpR|No;tRZ=u?-n+Qy*PW0C0M2XU@9 zN#8@q|9V+8E9kMjVA)MrruECca3(~hjly0% zK3)c$KyM@ciqG5WMr4PFWa17eR^BrD1Bqrbmx9>}l7epTE-3)R=4P;B&(X`JyQHJ< z6>=J!W%57>%PSgkbi52qwt^``pj(p1bxPQw^yj86$|8?&!46#JHy0*2?e=NfrGnq8 zVm_Ewc3iC~kG;g2Mk|~tSXVZf>9N#E*A6$Iq(n=9iBVET_)~rc&TF?epjI2a7ly*= zIxZ&Bm|cZm#GU}rZoq1%)_-x(G7vqvS%+tb9$^Stk{1nEt%)VIk))~Hh&!^mf-miD zT;=agr%1LM8-*d2HX)I>qP>+)Q?j22#jDsNpX3$!7cjt*Vh`$-&a)$j6XNc=0@_mQ z1m`B=dbDAb(L$%!ZGKkCXz3CD5)xbAs~1J&+!Akn8gdi2zKb(6CWU=MWy`_0Q=hva zcj0ATA9!CSd)^o}_Pce+oi$0!4b!Y&Cg_XT;&6{#^HKmQ?yn2(FDh!i6F!^>QUm0U zTPk?h7mM3I*Y`VPddE70p=(XO4U1Ztx2OC_DKq^D|2k;vt_QJKP-YF~o!UWz$oFo8 znZAHCkUVinQME}TF|Pc|(m!qiJ%1e>24K8>g<9nH>KD4wOv9&B*xv28LP_%`v%H@d z5L@3@rQrviAW=1*z1^>9b8uiPG3Jaz`rv73)n7$T+5F4pmyzle<8Nrt!$L&y z(0%g=YEh%2(?m!6Wg5Y;QJO1^;&LBP3sI!FP34bhYDHODw(_H0;y-*0xue+&Nc_2s)ix@ffc`!LJKkjTS3n2n2FQ~hx+rOMRHW!I>$#kS2vD6F9OWG)~ ziYe-+d=r8q@uOG@jpOXgluY&S+G-m1;|HM;Yc{)vL>!0R3Yf$OBj56mo3^UAFBeWF z=m>MXZOujudC%91dEROr?=}CHFH&X11Ls+5E{DNP7R*LC4VFD&G#0DlML78`5|Sp> zMFssd{0yqQMR-khFJm_H`V_J?A+Rw@v~}v($J{1uIqUQ$KBf{5wTfc6&aXECKw*OO z5R?eP+trPV(hayVy_JzNtb!{~|1)FP-m|BnJZ;&GiQ9rsa9x@>cQLI*o9OhIuWeKW zV!f@@ett;?P@(F{Jf&-nYLYo60MTnzG<(RLwnjaqx@sh2f zWDH|+{+jT$i3&4QN{w`aEduhBXIovF5ey9Fb)>jtgiH@>HeVs@1=EjvrFOXBoA-@7 z@gEI^YBRn>#fTapUE`2HzlbAFj1XfXZzf#E0Mhl3_pd13Rp+x;@xK*d+!m)P-VjOW zko;=~QcwJAna1sAdPArbi#r>IstS59DrAZV308-fmL3k05C{^4$^kfjV|Sr#$1jAk zM8=^q&N+?byJQ_Zp@bJ%y*ws|<5Pz5Mf@nP7LYNpLhb?>ZEnz*hze<<;!`yWc?L1k z)_5CS%cRH1W?Otj;<$TLRzQo?Zt|p%Q!deOAccP7eJ}Y|+8Yw#u9cb13z3}TboYLQ zlVVt>GIydP(Y3H(Xp3|9W-?2nl>&`=`zoG(+OJayk>9?my*1%)%4jM@jhXsf9M3R{ zw?cnzJSzCXZ`d8&9fdrK>%6fhz z*<^{r+&t|;Lb2(_-MlB1R|&xrpV$s()#BxF1k=u~(8|11drxh@VL|lflgFH}xLXJ= zEJs;m=HAxUwnA=?VWLscM2hE|$7>JF$ShSpvm+rrjLarlIbqYTc7lM5r1Mn{r@oV; z$$qGPIjSMyrirESfeSLiUCv5a9eJ=g4w+u6!Zrq#Edtzf3v^yvH#-{qeGO=-JDcQ~ zEHnDpBOBKDBBGa^SFRdaD%|_5H~B-04SC_wO)NS(^4ZyDAlTfo)f*3=XZfQPib}sl zX(=9uDmC1N@AjulyOenxK>8@WT;XepuUp&EM@8ZGMlKY%FUA-6I@pkGajx z$-w8^)?niGr{njk_Gs5^e_N0UHUbZ<&oS3q`Yp&-Pc@;iPX7kv2`?=5&^xv@UT>TH zt-6)X_#2_M#W3fmit@|H^ z_c@L@zGsw%{+p!u6wNQv6h%d0t`!&a^zLeRy~*U zT;hm5@f`aJ<#|%f4LBtzlAr(Hyx**U&Wj>y#4p7l(rPBjOS1|g4w12T7>f$4?Ksn} zvV_FK$N7RH*FH#;&q!PmLY=ULCo>mw~l0SY$yCO@MtueK0-8Z<{gH{9O>s~z+5(55k!SsZjXPkTex zf3F9=JQI0Bb)=Bb1o;{mIu0z=qiPal2vxrN0_8+frdb)#qU26pr1dOCo{uVt#ev*g zWE~0$_?KMf(qiuMhYe_nxepz?JgTAPsri0>P7cC&KI=4&SlqCmkBe3Xk>?GUNYlrc z{&5~F%B9)S-r4f84cE=oZ$J@6jwXLUkE4lB6p4Lbih}bwigimO*RA5aB1%GhbM!E| zdq)1=>|Nav`TNCpm21ImUW>>8O6RbU{Ig;gBZzU<+h=Ap_a5Fvl7aj2I1<#?^Tr*^ ze@}4Q*S&UsO-WwW-99PRxE*1bObco5DnBmNbP+|qbg2-fKt!`&BET{r(@0NFd6L@< zO0#mNH|q$x{^Q-{`>==mT8l=-5ae0g9chHNU#y#GRx0l&GxPqf&nHhK~) zL~b)mW^~elg!K#W`o$hX>;A>(VPlLy9=vt+Fd@34tsQFd$~+VqYioFlO{(FAmV7Un zG`YH|qh%9t!}&43S%qrdDHtenKKty`y!|W^?9y${_%z!4><${&@+37sHhZi4KC*}9jE>-mtL!l$ z)&Bc#vU;ihU3E3H=A@~6bN~EN@^6aXOO3nu-sfP6>wDq?)B;%cFfA)ko#{h{hx&H_ z%AQLS;S-bV?>EQI>B{RpWY(W@iSjNj)mL#ZFy2& z&Rf%>KOO}?6h6b3&25xwRcl;%s+YS~ot{Z!SOx;)b)57yfbTIn;u%JE$7y9A#5;uU=WyE+AwXzbyT#2O|Bg%g>2xHea1I^LY$C7y$AI? zG*83*=)ldJS@#&6a{kCS$lT$En8^%^6@~}h@u!M}64#8B!|%gZ-Y+^0`~b&wGPCAJ zsLrN*tWQbtsKO?s@igf@6+2nXM%w@{Uz;OE$^)S?xdRi_HSJl)L*)D9pU0k$2ftTrpZJ5r?|?Q- zFYl$8Dt0loLeb>0X$RPl>c2jAKXkqezKl`uo*9@{=tsC)*hrlr_3F{fz6|OvBmFkW z#xKQ3G$m9<2e0#~jC@Z!t5U5qO@AE^!;msC@CA{_@Tr1z-qG0gJtfdBYg=i|)3!(} z6l;8`NyksA*6lc`K*dQnCmG_CF_6^r;G6|(A3Wj|dHJ1*(icAOgKJUhDbx5Q8H`pWHM6J^wW2(h>L(xDj_s;|ST z8p>{MSkP1@rlSV>!!>d4Cqa>PX=*l=l}F8kC^5V?0vh?u5&9vdwV3L-5q)U6xE!RN zcO-Gq259TAkg@!HjXVBw&Mjg{4JjKZY&N9L5{^;KA2GS1GsWu0EMd$aQsCvpvZ`rm zDvhDl#|GM@925jm?1Y&&3`&*45JQx1)r(KkIy4Wj z$5Ef7yJ_m7=>sslZh_3AQjZ!Uia*uF{8oPZNE-FJnj~7uF;y` z(6Fd#8S8dp)XaW=TgdPz_L$*O&*hVHQ;e>|_)YJl=2<*eC@nvBxnTDLbhS>B^4Fqe zaNl{La`&!k75y(?LCaKnbSE*QDnckGTHi24^W1pb2`P1hgK(~>y^fT9^d^Sov2-z< z6G+u2u^dO(jLC21vo5Zs#MNtZyk!g;QHgvYR_W|X7M8tgAU0qg+{hf7nX=Y+ILv82 zIrdF(DKFlBtSVN%%t7JOFe+)H0r9Gf1Jp>pZ0O012Wu!_3h1C z-h6@X#*6RrFL|TwO~yj8h9ioxq_iMqKU7{be^Xw_^q3B%krdo17AweSs*q1q{vl7g zpx9yIN3iIzcrCjZj^=-QQp@`6%1#%9At50j%J7&UcK~by7xML3QUg{FB&6SP6m^z(%)21JrGC@4}#y2*2h4e%X$16 zlu`>4y(1R6nc(eo{Z$ZK!KUdAoQukj;40|}$1z^s6>wsX5mk3VyJG+&$9G0eCk+Ah ztPz!*>TO1Hteat|UVkkS_lY~!r(Zs=g5Z{d3AbCMgf}As{H!TsF;uh(_dB(9fidUCcV02 zg-(S8VFpAcqQdi)Bj3OWoy6aDd*BFYa*@R!=9Qc0AOo_ALKMSqyZ8<1#vAb(k9?9r zcoWA09A#$HQMXJ?7O6YDj7oFq6@###h21V$23kX~6+@>1G1RL3G3L*6LWQ(Xeohwy@E&idK(6x)}QZj~}IKBiId#B`asY7$~APtlTRGXoMNM2|<0BtO!t z5jUA^#psV-76S{AkTjq0UuG;V$cpU)ipFZLG=XBpR=auTwwQ^mN<5zJf}Kq}f9D>z zVTa81%j_S0S}j)RMii+u!kd{y`AjT;XRxZxrM9K? zQ?mT&y}LY%Bc+ltv4GgTPkz9?L!|Yf533?6mom1epU>sgJM2Ml0TKd4c7#8NJ153= zThjavu5InuSwx}O*p3`Q4u%|k@!9tdb@ssUU0bYjtX5(QllS6X&3GCr0MJ=09HVyp zCqX{UQaIj&OuLu6I%Sd(rQI|SivgRAl!4x#{MdDcQ_%v#*Z}=HJNW0p;LuPjA5&DP zt&l!U^^9~_VUw}<#@tJd8Zj7<#8qpq9$AtX695BGWQduFsIdhFGL3iAD+e}V9MTlk! zE8iCfsuiITQTIeL=n+7pa8D72%qhk?~fn$?#N4Ac`N z0vs}Ho=ktZ>o1}`H^6?uOY*(r*q>C>F0&()^pSR0r;)^p4`XjZvnH#0%=qTiZdImq z(xYOo*Mn9z|z@Zi@X?mEgi?Lp9;!MLUVZekq2f_jLy$0jKkYvAVQ%s{-B#b zYW0DL6FP{(9*yJkgt-sGj@;Lf?FN~e-QE-Kr#`%)?@6 zjJ7Jyfv0gPmP7i7&Qvu^-J>%Wq$3EIydiaAgRJ(X-GTU(^&x(HCm&FjRo?Fuj)HN% zmsZk-oLuIV?07=Zn!DNGim?09K+*79^vM~?RV`{yP4B7luS@WyD@Z|sND3%^NTQd!Z}RsSY^uN8xyp&k?Xnn`}CFnAQqPKvPT z8HDO8gxy##l4`%O+|bRvgBm^_M?KCvTm{pFX&{eA8HFv0nblV_`*^{b17-JW6qo>H zMk)Zsj9?k-t+w$GVdTfC>aTx&m=OqpSnM&Fg?tZ*tell|E%N1;{TsD07YUB6#F5Wc zt)hzSCDHg!COvFb8oztUC^4359mXANDS#Crq4xZYL5dV)*P|i5W zH(Hu46$$We@zw1anj|0`Yj(tSoMLLMcR%!-c;6lc_N{V*D~{c|cpypzW(8m_ybaCu zief*O)s2D)0|J2nHf@QS#KSu*D_1RxH21xa+3@)WKzxYKN?_Z_~&z{SV?MkTF~60zrwBZvC_w zy~q+{CS6KcQ();DZvl?G#2h$*aWI0`oLAGcgq-`tE~{Mt;r$1TrzJp@P-k|aN zi{8&A?t}!0Dg}#Mh=5f$pS0s7ZF=gd@A^5mK%WnDfdg1k21-6mZw~wSjq^wvdcZ`v z9WS9eh3AjmG6Ux-PZ_N0SU()ijPD;-f2zU*%6O#AxZ!04pT*2|1uzG}Ks17=gn7JF zR%|5<;lP)_dZwEcR0TYlQW49T=2saFB|mUuFZWiRzsIzDylNnu27Hl_-L6t8t!?DA zCY2ahv?3Lpd9sHsa2+Oj&+5_TrX>KXaujgP9T7RfYF{wvNKVrm<^6CHpPdclyYAG8 zCwmxN$B-cS;S;4m51}-xLTd{xX>*NrsfFAuNfdWlAAEiFBkKGw0&`O+?VJ1=^PH;$ z>%rywMVvt}q8$L=sxDU$`3Wm3+0MRNvvlMoh{R@l%F(nSJu*Q6L|!dMY6sgp&nuXM zDL}2wQS;fc?%@vyTxahF5#CRnM^{&Ki#?GU7?AN;}W z)!eY1!A^;rs>4FqH|*|l!G7>0<}5Z@P0EFUNb|@+)%%*=C-sZbprvQY*Lojy%C+lM zYi2CXz&WE}?SE~P=o3<#W=#f=as7!HOTS&d2Epi)@?7OUL}#xWNoQ2L zGG*tC>m(`|qT8QkDv?H+lG^`+U8oC{ajU=z1OXPZulR6&%hi<-?BG+{#bq|dMxz+( z5wjHHMV-xN)`Y*yV#X)OFbfeAyvSqBdca>b*H$=PYTE8xK~}`tl@bzKZO%7^2LD4Y zjQCkdWe>HuYD#g?9EuGiimMo2fH&SutMl^Q(wu~elJ0zi(d6U#IZ5(ywdLT_zjQbBc+z;6n3Y_I<6gAs7;}^ z-6Wwk=@b@sI$2F|y!OEUnzR0=kr^P&>^ z6j3x?@ab8z48KFY4v-=Gq3m``?X?Z1Q{0n|{}nzJ4@bgO(m3H#!=*3}?YfTqUq z`Qg^IFilsNS^2TJWL9 zcCaWpSIGGOGd2s#u2Lo|pa@nFb}5rTs`8%!1Ec;atBe!)^xPc7?{#GvQmr#d730cV zU9B34U)D||WQI|_&Ewf^?fT9i52xq&oyDztFBCnLQt*W<@i53A>9sWd0THT}npWaP z%4Yz;x8btXDBqbfBzd6+IP7O&thK)C^jH&DMYn0d68?iTWf+g=HTS~~?J1=-nULQ) z6OZN9v4UE~??4u(S`X{p7I5mv;NI2R)!wK^)b#bl`O55^Y^v7=dbr_a)#S{)Qmx^C z@Al} zAeUsQxEs^gNv8qKYk9>!wq}U;(;!p&;&Ez{RsV!~M_;hvIoRnM7pKjYe~(rsonT3-OGm_R}*&PR#S2aT@P>dKHm0TIAyjF z{kCdQNZSjnAT46m%oynF8A7yNC#hq-E3TJv4i}k_R?jzjg9@yVxV8+&ftY;oiQ);F$)FYp)tn^_% z979LneD3nbxli;l@GbyL_=M!cWdA9SA?Z~^gDw|os)hmZ1p4FK=N*%uPK*UsR zq-{#g`ZH2^4&RIO@J~iG6!AQDT~_{D78UzWN6yQnQ_e9pyHWs;an z(wDEW2YqO?MTfx)5pn7_Z-`;vJc{=Y#CjoUm%_Xp6u;yfzTRqL)@LozN4Z*MTaAJj zBsbIWqs*$y8l@`}w6|tnF?2;!&lMS^1dUlXX=r?Xd3N@Bn&z^cM+pxPy_CI+uQSKA z7Qs%j%{>bq9DHJtCqu^}s@p}?Tv4V_{Bkm`1T8i7dAXCmrBn#R6NPi^HfnV=tWI5X zS#z|9bHtq_5NDg->|dL}sv2`ZUF4G4dpnnBG$R=NS3O0K9DjX%ogOw0vgYN7c88jh zCP(Ld!`{~Ao|JvO8hLlaiG5?fG0pP>p^NUHE?zY{^*AYY@2{B?0$f>Vg$#N%vj^Dm zvp|ZpXSm#qIsRvIJPDJ}DW-V@|B$Ngs{K3I|9mKw4_6C#q*&MMIDC)v*W}~|`2C;j z;ISD62QtEnMX9CaHBqO9`aL@vC-qL3?J095WJUh_X3HdELE>G2pKML}Z7$~5$^37h zyr|CJ_SDx6np5idpDZQ(eC&q)hGGxuU?HsCaNOK}wi`#QO<{IYwK~Y@)ex>bY2j(_zQJHfMk8P6%VKRT>& z*t9Uk12jUYB2;EP?X5n zR$F&6ZjUaz>5gq8aPh&#o7{qa2Xrk?FaD)h_g8@7r~*j*x@u0F$iJ-r3@AD!m0y0t z*cq(C{Sg0H!)RM(N~)50kEm$a_;@bQ<*Kq;c+y{|b!VWdQ^lKk#tQ9@JcnSlloFYL zC6g2XK`5o!O2{w;he~{jBA+)b-tO#KDM5qXCjtp8ggq0O5;d5I65AHlq+=nQG z;139LGN|)fYm5Ce^Pfd1#g}PcbL4naQUwG<18Yab2x)ZSsJ!zp)Wh>(3L8>@;0}V7 z;}Jc?Tq@@3f$k65YYjN|Nnt}41Bms+>5=5+-@BM?;VymUhUBt8%~j)>GM=X)xWHm# z#3=FhzxMIN>ADbnSuewt;pzFt%1$B!83U)4g%7rTiuXV&~5jIyi9u>d_1_up?$*rnNCKO>`IC%|A^^qg=mbPo*HPrlcf^i0b-xXTXC%{NJ7H z41(9#T3U7N#DS0_JX( zhzo!V%*D$FhOmLTG{JlVoLmCD+$>$28x z7Q;E+UAwEQt9I3iRaKTjMJ7T9005|RvLJN;09q3A5krKBe3MXT&I7qnSjeg?0RaAV z06<6t0PqC)D&!ad@L~r5PE7#-!At-E-!->WO$hQ2cyk3A5abH^`77BK47nh=%IbLl z04P}hK2UQTK`)Rm5j^FTq!3P!py7c$rKmk$AR_|gK;oLdtLLu{-<;OG$>0O)?m3qA z0}7S{e|WO~9bZ~(sr!|_&p)6$K!-cd|LeONlT1ywXjR%2;^QH%VkI$P#qE8;(ha|V(mvS9` z{@Li7)_0h?IW9@$@PGGrFmXU-Cji2S_nZFT<$o6h`ERM0~2)Pz=?<_rsPOnwOA|*S287PeCHF>U7K>IyEaFgH9 z`_A|XfC$9`UNFEYg(#1g0))G1Ka)cVEx6hJ+)t-N6Ot*FVB(tB!>PNI{>rf_Hpr;# zw|p_j0b2KY*xq0JU2oVqE!lH1Knu{T>@cBc44U&GfBVbZb~lrKoB6*nwx9uCrw>$U z-X*9}so>G1epmGw{lS_TEVWF~gyB;qkOO|afgV+I2hy9hov1PMq)uL}7p(0#D{7%>FwY>-vJeFhQ& z>7^+}q(j(HhQonT9-$p$z$k1gMCY^7-_My8Uo3HjV4(;L!r{pog;h0x@L(zeLQVo@ zMC5VIN~&@^eX=jj7k#`dY(q&brvn%8Tk0#pmw4$Bpqr>QVNQZ6=tDZl5)nInn!e~o zdXMx5wtf*SzJ9suFvuIA5GEP8BM~JJR!l*ZeZ>_SWyo{7ESG3FY-FD%v|@JA^?oZ7;VpyUBQWgqF zhrCti6PxI5%Bg49PifWfOYG{9T_pNX!kBsuEKH0WbLO8oQ?VEuIyq$7Nu+!HD_@ZS z)f8pAIpDN;c(6eKf1vm0rl>AsQ-kToDceC1UpwI0n>v~ ze=c5-bvyZ>EGHK{Axf+l=gBXMA%*I@ff;))(&T?OG+7HPMnTZ2 zS$-gnjf6noI)S+^E1L1V;kmOfBr*bW?+L4;&d@@H_J;3y?h#O|$$hC+0N8 z8NNeenobE(pF$X18jrz)Ub((G4;EVOkxifKyrW1lWhBdD-|!vae>bHSf)!y>v4x!~ zv^B6@>u-5HCPC|(UVliUF%nBIpT+Dx{ju}Vl5$c7ZkGU^cfri)q`9 z9@2RZYgXH$cIuIP$F+Pt=YD5A!Avz@LXLb*QIS~O-oT&FlI!D1X|;9AZ6VGFH&C`f zFIXDhyWrmA^&X1B+-8nM$VC8rK%kb4O6|j(C7yQaz0z9ip{*9emwls|F?hh{Ks05? z+&`?d+48wBveETv!|<(i`PS=zi5LAC(!RD)$mvSSx=w`k^hjI6^TV@wvD~ zN{jhIVe1L@j+L#9RnRVo2s_hVappLFQb;}^fCZWde4^GL_c&wO{rX6)V}vq$Zk=Vn z6bQnpPEgyR)UxtF;)k967-b;95OVJn-KFDmZ0zSc-|gMf`S+!9MXR#&~ z;e*Eyt?(b${M-+eZl|o;YOn6gUIhVtXIupSzMa@fJGD1E%O=6^IadL<8NnA0CIRnG zYf?TiT9K@YF&$>FI0O2CT4dEq!=T$Gp4Um0_u`&t{Lfo|!nbNR+KlZGNWVIfVr`BP zN14?6AcxezD%Qd-a|a(TZfuYXcrl}9pg;^z8TwSe*M9x<@G0)96K$xYWYPXz_Gm!2 zq`HIZQ$ z_GwA-JKlVpq2Ovu1aRMmj#M8qpnvdbw`TSxj2NXWu=b;$*XbFLAur^#Q4UMQ9Q}?m zpaEqcd2$mwb5c}o)j}d7rb{QP-4?y0i}8gc{As4K5Fz4FY>b4o@}}t;xl+^%8#MzJ z<%ByI_6oW4id8E$y+nt-*Igh9MoO zEiOZZI13|jfu{T#}4Gj}I7)9@xo{!6A@~ZFd^}Q7-5R?8s_%lJC^JdNjo&ERQJVo8T*X!l=nqM zjO9SOy~QK-iSc#*F4L_|2H}nR-}hLUj-w)VmXzF45t)AMmgGjg%bT0YOH!((Gj+f)>XoOZvbF2A-l5pm%B;(0~A@M04mfieQ*$Wcbj&V-V1KD-^cS(7X= z0cON|j5d2c;Sm5UBK|3qxGqmzjDMy7!cv9;VjYqWLm^7LHW;rG28rUMK2nCkCpzVh z{%X&HCHXH<@E|Gr1Fimjf^P%&QS*?)H!4-U$>K!aK2^y(Y-iW4BaL6Pu5HF2d}0P! zKIbtuvx;a>aeDs`H#>c>6}5vsTM}9&H0Tpvf&|zKylx43T5FcgnAoSqiui?}7ar)kQ+{QyqlX7CjuOc==%|*OJh+dB-?O6J*FD36azqvhx z?&!ua%J|52w1blqV6*`(Ykee3cqs zz4W1yuzpFLeIN^CE7c~vKDiKaO-!?s+pFlPH_ zo4Y=5b?!85-jHSHWR@WW)`n%T)oRLE;6>Q#@N(t zHYBzlU;b=}<~S~#u3kiTKT8q=u9?%OW+~!4uOa_R#;v2bONv4q$mH)0xBHt zVm3n!WPo7&Xa7^a&kWA=%nJ||q*2vw zuhE+KiUr<}4CHH2py%q>KHHVBS{`BWX}af}sB_YM{bn$7=CCM{&Y(@@#_W-5Bshv9 zn)h=r2MbWt6+oc;XS^r|>BbBqZ#Rh_ z0nC8FH!Op!ws3)AQVTmtDXevS%{)gs`-*7#fhb_nFQI!1aje zTP%VJ>8IU2a(cjI`rn&kGro6;@8)lN%rfuiaev`5L4CH_TFl5-QuNhsYhz z$MH6kyiO$gk*oKK;OhmYzKQqw_v6x|nqJm z9khkl@owCGG~%`TXK6*)#ciTpPs=8G$gF_Pp3o=~Sk%HZ#V~bODJrphn__)DTPErT z^B$G3{n8pS*IAe^DDaM7n|KWgE(xWh*zg9Sq3lQ~s@=qjW>ELSoFCo&w=lfxWwoh8 z4-M%d$>m&8^|d>~mwl_UTB~ z`t&M;wMv!dP_v?q9fWNFKu68t%EHQT2$;_J`9p%N(DRHjQ0gtfN_=3>7d8TPr%bt_ z?~+wiHEYi71Xu&Ug{fsS38Ko12U+;UNo95)28- z8}N_#rTxQouV1F%^IS9`YNsy&VW;7`6PCmW7$*$<2N`_Q2!Ghr$EvPAND>31Neka! z5zP7hXRk~}tcC5AS&<5)NRhgL1iR2dj{0U!B-Fq*9T6#rl_;gA}VD>pwZHr@%Il)7aqd6l!U`kBXaOIwyM?uVqoq#v|a#12J*Zcr@ zNZVzZX>~lnv4lGGgDztBw&15)PV6){>{Ml= z&UB+J5Y(IPK4niS;0#kMbo#EaL$&OHzPXKL3D0kf&ZXr0XHT^p)iKG@LSYVRh^`D;4 z%EC-*f>!84cfy3oxOXL9HLBp2Axi7DSTJLLT;e(@KBetyd=T?|IefDlvoaWIX` zE&|_B+2F&?bkg9m<~-%mlB7>&0zqL}%$IX!md6s}0FL=tLoLvPf_ZBx)NnQs9tI%W z44mzQD<7-KNBI2c!8WHzFJweRz5L4-8z5F9=5t!lo{M-$-J?n3M`_tT3?X^K*rR~R zO~b&2>|)ic`H=Z`4WCd5Gc+*+C#eCi4Lw{k3GdH!U|}FAI|Sw=sQ*4PZORUtW@g2d zRI)^{w0I#SLl|88wWZ3ZJ0&(dQ*3+_&np83Vs3aa;=52?SFHv(xEx}RQF8yRwDNbZ zie*9+e1_ir7#c#sV^0$0B7vE7?e&hqmC8hX@5^GFil0BAk6!exKORe+kg!vCVWk51 z_3LtZAUAe)kK&SmJ#4uiHvteOdAP>HEv*rQ8oB6RoBFu%A09=erPZd^mB{HpPMp_2 zf3JFw)P7~|;Bm_}aP{L2X7B1NnLX2=2KIiJmfjTr92x6`KuQZs&BN8V1ubE^!( zGPG4YR@__vBndkeDl2w^d^%6@x;RhxS2``ffsB$w|nt`7l?R$RNI+*GR5F9s(wlaU2b$5Kj}p+fe^~blBWA4HuzG)Gq2)-DGB?Y>oZ?0-;_o}#B zT5zz{7|F`vgfiiNQE6GQ6k1zy^rs^>NQQKz88JAp`gPmui;G+xsqFlf0~(F1G^fQq zVoRp9&|=VK)!iN~YIUNyjX25Bq&yV37&y~g{@$n05MF!=$`f@CSCAcmop~d{S0DXO3L(R?dV4DVJ=tdJ={R%HBujFTI8ko z;k7$VL=N_z>Y7O&q#D_3L|lmht)T`dxb^pXpjzG6ho|>R-s~^US5aY0H1oO9=rn*E zDx*O8VcbSef$ux<)qCpJ>&rsmY)vvU|)z^gH;W2G3oKo!>Q@2N39{H^W5z( z#8^=!K!*i(BY?w0Ck`>D8UR(SK5pq*@c-rx6DOj)eHoRDe>px5n`CQ%$bGC?^>a5MB}rGsagH z=BFzoJLlcLICk*bZ-{3kuunVv%*$&`^%*Ekb-c?xZ0KXvEkf{Vf7}i|M*FGNpc`D{s(+(LVr*pDs%b;TeB^( z{NE@=YFo&%aCeJmhlk@Kd1JnieGP{AEo&QY5@amPeRt`9b5C;K#i-T_bg>z!-ye;I zLxu|v)ls!GD=(UeNG_6rs|#W?=wT=`oqd9%pD2THsz9F6qEa#rL!wEgKZ6{sg}jh@ z6_^0WL4=46^&c+k`riU$P_1OL%pY>4Aa#N3v?BZY9a`|^Tk1$|$SOn%h@qKgLtqw& zPjY@VR(tLXm&q+ZZG+$h9ts;3FQo9QaE{x7{Rh=7y~MrBKRiI+U_emc?Jxno*9R`S zh6*=hiNV+X<;=HPLP+Pg-MKFUkg7-&SJ;=>f5(0z9vI(;Pr|aH2l6P6t%;gxJA0<^ zwVK7&C}+SPD�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 Date: Mon, 15 Sep 2025 02:44:12 +0800 Subject: [PATCH 15/18] feat: full migration to extension --- Extension/Script-manager.js | 7 + Extension/auto-image-styles.css | 159 + Extension/background.js | 444 +- Extension/content-script.js | 72 +- Extension/icons/icon128.png | Bin 61717 -> 112618 bytes Extension/icons/icon16.png | Bin 61717 -> 112618 bytes Extension/icons/icon32.png | Bin 61717 -> 112618 bytes Extension/icons/icon48.png | Bin 61717 -> 112618 bytes Extension/manifest.json | 13 +- Extension/popup/popup.css | 530 ++ Extension/popup/popup.html | 76 +- Extension/popup/popup.js | 595 +- Extension/scripts/Acc-Switch.js | 8538 ++++++++++++++++++++++++++++ Extension/scripts/Auto-Image.js | 272 +- Extension/scripts/Auto-Repair.js | 717 ++- Extension/themes/acrylic.css | 26 + Extension/themes/classic-light.css | 20 + Extension/themes/classic.css | 23 + Extension/themes/neon.css | 116 + 19 files changed, 11363 insertions(+), 245 deletions(-) create mode 100644 Extension/scripts/Acc-Switch.js diff --git a/Extension/Script-manager.js b/Extension/Script-manager.js index 98af2dc..a5fbf2a 100644 --- a/Extension/Script-manager.js +++ b/Extension/Script-manager.js @@ -34,6 +34,13 @@ description: 'Automated repair and maintenance tasks', icon: '🔧', category: 'utility' + }, + { + name: 'Acc-Switch.js', + displayName: '🔄 Account Switcher', + description: 'Temporary account switching script', + icon: '🔄', + category: 'automation' } ]; diff --git a/Extension/auto-image-styles.css b/Extension/auto-image-styles.css index 3e45a67..3cb715e 100644 --- a/Extension/auto-image-styles.css +++ b/Extension/auto-image-styles.css @@ -1920,3 +1920,162 @@ color: var(--wplace-icon-palette); margin-right: 6px; } + +/* Account Management Styles */ +.wplace-account-info { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 12px; + padding: 12px; + background: var(--wplace-secondary); + border: 1px solid var(--wplace-border-color); + border-radius: var(--wplace-radius); +} + +.wplace-account-current { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: var(--wplace-text); +} + +.wplace-account-current i { + color: var(--wplace-highlight); + width: 16px; +} + +.wplace-account-current strong { + color: var(--wplace-success); +} + +.wplace-account-switch { + display: flex; + align-items: center; + gap: 8px; +} + +.wplace-toggle-switch { + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + user-select: none; +} + +.wplace-toggle-switch input[type="checkbox"] { + display: none; +} + +.wplace-toggle-slider { + position: relative; + width: 44px; + height: 24px; + background: var(--wplace-accent); + border: 1px solid var(--wplace-border-color); + border-radius: 12px; + transition: all 0.3s ease; +} + +.wplace-toggle-slider:before { + content: ''; + position: absolute; + top: 2px; + left: 2px; + width: 18px; + height: 18px; + background: var(--wplace-text); + border-radius: 50%; + transition: all 0.3s ease; +} + +.wplace-toggle-switch input:checked + .wplace-toggle-slider { + background: var(--wplace-success); + border-color: var(--wplace-success); +} + +.wplace-toggle-switch input:checked + .wplace-toggle-slider:before { + transform: translateX(20px); + background: white; +} + +.wplace-toggle-label { + font-size: 14px; + color: var(--wplace-text); + font-weight: 500; +} + +.wplace-account-list { + max-height: 200px; + overflow-y: auto; + border: 1px solid var(--wplace-border-color); + border-radius: var(--wplace-radius); + background: var(--wplace-secondary); +} + +.wplace-account-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 12px; + border-bottom: 1px solid var(--wplace-border-color); + transition: all 0.2s ease; +} + +.wplace-account-item:last-child { + border-bottom: none; +} + +.wplace-account-item:hover { + background: var(--wplace-accent); +} + +.wplace-account-item.current { + background: var(--wplace-success)20; + border-left: 3px solid var(--wplace-success); +} + +.wplace-account-info-item { + display: flex; + align-items: center; + gap: 8px; +} + +.wplace-account-number { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + background: var(--wplace-highlight); + color: white; + border-radius: 50%; + font-size: 12px; + font-weight: bold; +} + +.wplace-account-name { + font-size: 14px; + color: var(--wplace-text); + font-weight: 500; +} + +.wplace-account-status { + font-size: 12px; + color: var(--wplace-text-secondary); +} + +.wplace-account-placeholder { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 20px; + color: var(--wplace-text-secondary); + font-style: italic; +} + +.wplace-account-placeholder i { + color: var(--wplace-warning); +} diff --git a/Extension/background.js b/Extension/background.js index 2c904b1..c2dc166 100644 --- a/Extension/background.js +++ b/Extension/background.js @@ -48,7 +48,7 @@ async function executeLocalScript(scriptName, tabId) { } else { scriptUrl = chrome.runtime.getURL(`scripts/${scriptName}`); } - + const response = await fetch(scriptUrl); if (!response.ok) { @@ -67,38 +67,38 @@ async function executeLocalScript(scriptName, tabId) { world: "MAIN", // Key: executes in page context, not extension context func: (code, name, themeCSS, languages) => { console.log(`%c🚀 Executing ${name}...`, 'color: #4ade80; font-weight: bold; font-size: 14px;'); - + // Create detailed resource report console.group(`%c📊 WPlace AutoBOT Resource Report for ${name}`, 'color: #3b82f6; font-weight: bold; font-size: 16px;'); - + // Debug: Log what resources we received console.log(`%c📦 Raw Resources Received:`, 'color: #8b5cf6; font-weight: bold;'); console.log(` - Themes object:`, themeCSS); console.log(` - Languages object:`, languages); console.log(` - Theme count: ${Object.keys(themeCSS || {}).length}`); console.log(` - Language count: ${Object.keys(languages || {}).length}`); - + // Inject CSS themes if available if (themeCSS && Object.keys(themeCSS).length > 0) { console.group(`%c🎨 Theme Processing`, 'color: #8b5cf6; font-weight: bold;'); console.log(`%c📁 Loading ${Object.keys(themeCSS).length} theme files from extension local storage...`, 'color: #8b5cf6;'); - + // Create a global themes object window.AUTOBOT_THEMES = themeCSS; - + // Log detailed theme information Object.entries(themeCSS).forEach(([filename, content]) => { console.log(`%c📄 Theme File: ${filename}`, 'color: #8b5cf6; font-weight: bold;'); console.log(` 📏 Size: ${content.length.toLocaleString()} characters`); console.log(` 📍 Source: Extension local file (chrome-extension://)`); console.log(` 🔗 Full path: themes/${filename}`); - + // Show first few lines as preview const preview = content.substring(0, 200).split('\\n').slice(0, 3).join('\\n'); console.log(` 👀 Preview: ${preview}${content.length > 200 ? '...' : ''}`); console.log(` ✅ Status: Loaded successfully`); }); - + // Inject auto-image-styles.css if available if (themeCSS['auto-image-styles.css']) { const autoImageContent = themeCSS['auto-image-styles.css']; @@ -106,7 +106,7 @@ async function executeLocalScript(scriptName, tabId) { styleElement.id = 'autobot-auto-image-styles'; styleElement.textContent = autoImageContent; document.head.appendChild(styleElement); - + console.log(`%c✨ AUTO-INJECTED: auto-image-styles.css`, 'color: #10b981; font-weight: bold;'); console.log(` 📏 Injected size: ${autoImageContent.length.toLocaleString()} characters`); console.log(` 📍 Source: Extension local file`); @@ -122,14 +122,14 @@ async function executeLocalScript(scriptName, tabId) { console.log(` 📋 Expected files: auto-image-styles.css, acrylic.css, classic.css, etc.`); window.AUTOBOT_THEMES = {}; } - + // Inject language data if available if (languages && Object.keys(languages).length > 0) { console.group(`%c🌍 Language Processing`, 'color: #06b6d4; font-weight: bold;'); console.log(`%c📁 Loading ${Object.keys(languages).length} language files from extension local storage...`, 'color: #06b6d4;'); - + window.AUTOBOT_LANGUAGES = languages; - + // Log detailed language information Object.entries(languages).forEach(([filename, content]) => { console.log(`%c📄 Language File: ${filename}`, 'color: #06b6d4; font-weight: bold;'); @@ -137,18 +137,18 @@ async function executeLocalScript(scriptName, tabId) { console.log(` 📏 Keys count: ${Object.keys(content).length.toLocaleString()}`); console.log(` 📍 Source: Extension local file (chrome-extension://)`); console.log(` 🔗 Full path: lang/${filename}`); - + // Show some sample keys const sampleKeys = Object.keys(content).slice(0, 5); console.log(` 🔑 Sample keys: ${sampleKeys.join(', ')}${Object.keys(content).length > 5 ? '...' : ''}`); console.log(` ✅ Status: Loaded successfully`); }); - + // Helper function to get language data with detailed logging - window.getLanguage = function(lang = 'en') { + window.getLanguage = function (lang = 'en') { const langFile = lang + '.json'; const result = window.AUTOBOT_LANGUAGES[langFile] || window.AUTOBOT_LANGUAGES['en.json'] || {}; - + console.group(`%c🔤 Language Access: ${lang.toUpperCase()}`, 'color: #06b6d4; font-weight: bold;'); console.log(` 📋 Requested: ${lang}`); console.log(` 📄 File: ${langFile}`); @@ -157,10 +157,10 @@ async function executeLocalScript(scriptName, tabId) { console.log(` ✅ Success: ${window.AUTOBOT_LANGUAGES[langFile] ? 'Found exact match' : 'Fallback to English'}`); console.log(` 📝 Data preview:`, result); console.groupEnd(); - + return result; }; - + console.log(`%c🔤 Available languages: ${Object.keys(languages).map(f => f.replace('.json', '')).join(', ')}`, 'color: #06b6d4;'); console.groupEnd(); } else { @@ -168,16 +168,16 @@ async function executeLocalScript(scriptName, tabId) { console.log(` 📁 Expected source: Extension local files`); console.log(` 📋 Expected files: en.json, de.json, fr.json, etc.`); window.AUTOBOT_LANGUAGES = {}; - window.getLanguage = function() { + window.getLanguage = function () { console.warn(`%c⚠️ getLanguage() called but no languages available`, 'color: #f59e0b;'); - return {}; + return {}; }; } - + // Helper function to apply theme with detailed logging - window.applyTheme = function(themeName) { + window.applyTheme = function (themeName) { console.group(`%c🎨 Theme Application: ${themeName}`, 'color: #8b5cf6; font-weight: bold;'); - + if (!window.AUTOBOT_THEMES || Object.keys(window.AUTOBOT_THEMES).length === 0) { console.error(`%c❌ No themes available in extension`, 'color: #ef4444; font-weight: bold;'); console.log(` 📁 Expected source: Extension local files`); @@ -185,38 +185,38 @@ async function executeLocalScript(scriptName, tabId) { console.groupEnd(); return false; } - + const themeFile = themeName + '.css'; console.log(` 📋 Requested theme: ${themeName}`); console.log(` 📄 Looking for file: ${themeFile}`); console.log(` 📁 Available themes: ${Object.keys(window.AUTOBOT_THEMES).join(', ')}`); - + if (window.AUTOBOT_THEMES[themeFile]) { const themeContent = window.AUTOBOT_THEMES[themeFile]; - + // Remove existing theme const existing = document.getElementById('autobot-theme'); if (existing) { console.log(` 🗑️ Removing previous theme element`); existing.remove(); } - + // Apply new theme const styleElement = document.createElement('style'); styleElement.id = 'autobot-theme'; styleElement.textContent = themeContent; document.head.appendChild(styleElement); - + console.log(`%c✅ Theme applied successfully: ${themeName}`, 'color: #10b981; font-weight: bold;'); console.log(` 📏 Content size: ${themeContent.length.toLocaleString()} characters`); console.log(` 📍 Source: Extension local file`); console.log(` 🎯 Target: as + ` + + const resizeContainer = document.createElement("div") + resizeContainer.className = "resize-container" + resizeContainer.innerHTML = ` +

${Utils.t("resizeImage")}

+
+ + + + +
+ + + + + + + 100% +
+ Drag to pan • Pinch to zoom • Double‑tap to zoom +
+
+
+ +
+
+
+ + +
+
+
+
+
+
+
+ +
+ + 1 +
+
+
+ +
+ + 1 +
+
+
+
+ +
+ + + +
+
+ + + Shift = Row • Alt = Column +
+
+ +
+
+  Color Palette +
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+  Advanced Color Matching +
+
+ + +
+
+ Chroma Weight + ${state.chromaPenaltyWeight} +
+ +
+ +
+ + +
+ +
+
+ +
+ + + +
+ ` + + const resizeOverlay = document.createElement("div") + resizeOverlay.className = "resize-overlay" + + document.body.appendChild(container) + document.body.appendChild(resizeOverlay) + document.body.appendChild(resizeContainer) + document.body.appendChild(statsContainer) + document.body.appendChild(settingsContainer) + + const uploadBtn = container.querySelector("#uploadBtn") + const resizeBtn = container.querySelector("#resizeBtn") + const selectPosBtn = container.querySelector("#selectPosBtn") + const startBtn = container.querySelector("#startBtn") + const stopBtn = container.querySelector("#stopBtn") + const saveBtn = container.querySelector("#saveBtn") + const loadBtn = container.querySelector("#loadBtn") + const saveToFileBtn = container.querySelector("#saveToFileBtn") + const loadFromFileBtn = container.querySelector("#loadFromFileBtn") + + // Disable load/upload buttons until initial setup is complete (startup only) + if (loadBtn) { + loadBtn.disabled = !state.initialSetupComplete; + loadBtn.title = state.initialSetupComplete ? "" : "🔄 Waiting for initial setup to complete..."; + } + if (loadFromFileBtn) { + loadFromFileBtn.disabled = !state.initialSetupComplete; + loadFromFileBtn.title = state.initialSetupComplete ? "" : "🔄 Waiting for initial setup to complete..."; + } + if (uploadBtn) { + uploadBtn.disabled = !state.initialSetupComplete; + uploadBtn.title = state.initialSetupComplete ? "" : "🔄 Waiting for initial setup to complete..."; + } + + const minimizeBtn = container.querySelector("#minimizeBtn") + const compactBtn = container.querySelector("#compactBtn") + const statsBtn = container.querySelector("#statsBtn") + const toggleOverlayBtn = container.querySelector("#toggleOverlayBtn"); + const statusText = container.querySelector("#statusText") + const progressBar = container.querySelector("#progressBar") + const statsArea = statsContainer.querySelector("#statsArea") + const content = container.querySelector(".wplace-content") + const closeStatsBtn = statsContainer.querySelector("#closeStatsBtn") + const refreshChargesBtn = statsContainer.querySelector("#refreshChargesBtn") + const cooldownSlider = container.querySelector("#cooldownSlider"); + const cooldownValue = container.querySelector("#cooldownValue"); + + if (!uploadBtn || !selectPosBtn || !startBtn || !stopBtn) { + console.error("Some UI elements not found:", { + uploadBtn: !!uploadBtn, + selectPosBtn: !!selectPosBtn, + startBtn: !!startBtn, + stopBtn: !!stopBtn, + }) + } + + if (!statsContainer || !statsArea || !closeStatsBtn) { + console.error("Stats UI elements not found:", { + statsContainer: !!statsContainer, + statsArea: !!statsArea, + closeStatsBtn: !!closeStatsBtn, + }) + } + + const header = container.querySelector(".wplace-header") + + makeDraggable(container) + + function makeDraggable(element) { + let pos1 = 0, + pos2 = 0, + pos3 = 0, + pos4 = 0 + let isDragging = false + const header = element.querySelector(".wplace-header") || element.querySelector(".wplace-settings-header") + + if (!header) { + console.warn("No draggable header found for element:", element) + return + } + + header.onmousedown = dragMouseDown + + function dragMouseDown(e) { + if (e.target.closest(".wplace-header-btn") || e.target.closest("button")) return + + e.preventDefault() + isDragging = true + + const rect = element.getBoundingClientRect() + + element.style.transform = "none" + element.style.top = rect.top + "px" + element.style.left = rect.left + "px" + + pos3 = e.clientX + pos4 = e.clientY + element.classList.add("wplace-dragging") + document.onmouseup = closeDragElement + document.onmousemove = elementDrag + + document.body.style.userSelect = "none" + } + + function elementDrag(e) { + if (!isDragging) return + + e.preventDefault() + pos1 = pos3 - e.clientX + pos2 = pos4 - e.clientY + pos3 = e.clientX + pos4 = e.clientY + + let newTop = element.offsetTop - pos2 + let newLeft = element.offsetLeft - pos1 + + const rect = element.getBoundingClientRect() + const maxTop = window.innerHeight - rect.height + const maxLeft = window.innerWidth - rect.width + + newTop = Math.max(0, Math.min(newTop, maxTop)) + newLeft = Math.max(0, Math.min(newLeft, maxLeft)) + + element.style.top = newTop + "px" + element.style.left = newLeft + "px" + } + + function closeDragElement() { + isDragging = false + element.classList.remove("wplace-dragging") + document.onmouseup = null + document.onmousemove = null + document.body.style.userSelect = "" + } + } + + makeDraggable(statsContainer) + makeDraggable(container) + + if (statsBtn && closeStatsBtn) { + statsBtn.addEventListener("click", () => { + const isVisible = statsContainer.style.display !== "none" + if (isVisible) { + statsContainer.style.display = "none" + statsBtn.innerHTML = '' + statsBtn.title = "Show Stats" + } else { + statsContainer.style.display = "block" + statsBtn.innerHTML = '' + statsBtn.title = "Hide Stats" + } + }) + + closeStatsBtn.addEventListener("click", () => { + statsContainer.style.display = "none" + statsBtn.innerHTML = '' + statsBtn.title = "Show Stats" + }) + + const refreshAllAccountsBtn = statsContainer.querySelector("#refreshAllAccountsBtn"); + if (refreshAllAccountsBtn) { + refreshAllAccountsBtn.addEventListener('click', fetchAllAccountDetails); + } + + const autoSwapToggle = statsContainer.querySelector("#autoSwapToggle"); + if (autoSwapToggle) { + autoSwapToggle.checked = CONFIG.autoSwap; + autoSwapToggle.addEventListener('change', (e) => { + CONFIG.autoSwap = e.target.checked; + }); + } + + if (refreshChargesBtn) { + refreshChargesBtn.addEventListener("click", async () => { + refreshChargesBtn.innerHTML = '' + refreshChargesBtn.disabled = true + + try { + await updateStats() + } catch (error) { + console.error("Error refreshing charges:", error) + } finally { + refreshChargesBtn.innerHTML = '' + refreshChargesBtn.disabled = false + } + }) + } + } + if (statsContainer && statsBtn) { + statsContainer.style.display = "block"; + statsBtn.innerHTML = ''; + statsBtn.title = "Hide Stats"; + } + + const settingsBtn = container.querySelector("#settingsBtn") + const closeSettingsBtn = settingsContainer.querySelector("#closeSettingsBtn") + const applySettingsBtn = settingsContainer.querySelector("#applySettingsBtn"); + + + if (settingsBtn && closeSettingsBtn && applySettingsBtn) { + settingsBtn.addEventListener("click", () => { + const isVisible = settingsContainer.style.display !== "none" + if (isVisible) { + settingsContainer.style.animation = "settingsFadeOut 0.3s ease-out forwards" + setTimeout(() => { + settingsContainer.style.display = "none" + settingsContainer.style.animation = "" + }, 300) + } else { + settingsContainer.style.top = "50%" + settingsContainer.style.left = "50%" + settingsContainer.style.transform = "translate(-50%, -50%)" + settingsContainer.style.display = "block" + settingsContainer.style.animation = "settingsSlideIn 0.4s ease-out" + } + }) + + closeSettingsBtn.addEventListener("click", () => { + settingsContainer.style.animation = "settingsFadeOut 0.3s ease-out forwards" + setTimeout(() => { + settingsContainer.style.display = "none" + settingsContainer.style.animation = "" + settingsContainer.style.top = "50%" + settingsContainer.style.left = "50%" + settingsContainer.style.transform = "translate(-50%, -50%)" + }, 300) + }) + + applySettingsBtn.addEventListener("click", () => { + // Sync advanced settings before save + const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); + if (colorAlgorithmSelect) state.colorMatchingAlgorithm = colorAlgorithmSelect.value; + const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); + if (enableChromaPenaltyToggle) state.enableChromaPenalty = enableChromaPenaltyToggle.checked; + const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); + if (chromaPenaltyWeightSlider) state.chromaPenaltyWeight = parseFloat(chromaPenaltyWeightSlider.value) || 0.15; + const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); + if (transparencyThresholdInput) { + const v = parseInt(transparencyThresholdInput.value, 10); if (!isNaN(v) && v >= 0 && v <= 255) state.customTransparencyThreshold = v; + } + const whiteThresholdInput = document.getElementById('whiteThresholdInput'); + if (whiteThresholdInput) { + const v = parseInt(whiteThresholdInput.value, 10); if (!isNaN(v) && v >= 200 && v <= 255) state.customWhiteThreshold = v; + } + // Update functional thresholds + CONFIG.TRANSPARENCY_THRESHOLD = state.customTransparencyThreshold; + CONFIG.WHITE_THRESHOLD = state.customWhiteThreshold; + // Notifications + const notifEnabledToggle = document.getElementById('notifEnabledToggle'); + const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); + const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); + const notifIntervalInput = document.getElementById('notifIntervalInput'); + if (notifEnabledToggle) state.notificationsEnabled = !!notifEnabledToggle.checked; + if (notifOnChargesToggle) state.notifyOnChargesReached = !!notifOnChargesToggle.checked; + if (notifOnlyUnfocusedToggle) state.notifyOnlyWhenUnfocused = !!notifOnlyUnfocusedToggle.checked; + if (notifIntervalInput) { + const v = parseInt(notifIntervalInput.value, 10); + if (!isNaN(v) && v >= 1 && v <= 60) state.notificationIntervalMinutes = v; + } + saveBotSettings(); + Utils.showAlert(Utils.t("settingsSaved"), "success"); + closeSettingsBtn.click(); + NotificationManager.syncFromState(); + }); + + makeDraggable(settingsContainer) + + const tokenSourceSelect = settingsContainer.querySelector("#tokenSourceSelect") + if (tokenSourceSelect) { + tokenSourceSelect.addEventListener("change", (e) => { + state.tokenSource = e.target.value + saveBotSettings() + console.log(`🔑 Token source changed to: ${state.tokenSource}`) + const sourceNames = { + 'generator': 'Automatic Generator', + 'hybrid': 'Generator + Auto Fallback', + 'manual': 'Manual Pixel Placement' + } + Utils.showAlert(`Token source set to: ${sourceNames[state.tokenSource]}`, "success") + }) + } + + // Batch mode controls + const batchModeSelect = settingsContainer.querySelector("#batchModeSelect") + const normalBatchControls = settingsContainer.querySelector("#normalBatchControls") + const randomBatchControls = settingsContainer.querySelector("#randomBatchControls") + const randomBatchMin = settingsContainer.querySelector("#randomBatchMin") + const randomBatchMax = settingsContainer.querySelector("#randomBatchMax") + + if (batchModeSelect) { + batchModeSelect.addEventListener("change", (e) => { + state.batchMode = e.target.value + + // Switch between normal and random controls + if (normalBatchControls && randomBatchControls) { + if (e.target.value === 'random') { + normalBatchControls.style.display = 'none' + randomBatchControls.style.display = 'block' + } else { + normalBatchControls.style.display = 'block' + randomBatchControls.style.display = 'none' + } + } + + saveBotSettings() + console.log(`📦 Batch mode changed to: ${state.batchMode}`) + Utils.showAlert(`Batch mode set to: ${state.batchMode === 'random' ? 'Random Range' : 'Normal Fixed Size'}`, "success") + }) + } + + if (randomBatchMin) { + randomBatchMin.addEventListener("input", (e) => { + const min = parseInt(e.target.value) + if (min >= 1 && min <= 1000) { + state.randomBatchMin = min + // Ensure min doesn't exceed max + if (randomBatchMax && min > state.randomBatchMax) { + state.randomBatchMax = min + randomBatchMax.value = min + } + saveBotSettings() + } + }) + } + + if (randomBatchMax) { + randomBatchMax.addEventListener("input", (e) => { + const max = parseInt(e.target.value) + if (max >= 1 && max <= 1000) { + state.randomBatchMax = max + // Ensure max doesn't go below min + if (randomBatchMin && max < state.randomBatchMin) { + state.randomBatchMin = max + randomBatchMin.value = max + } + saveBotSettings() + } + }) + } + + const languageSelect = settingsContainer.querySelector("#languageSelect") + if (languageSelect) { + languageSelect.addEventListener("change", (e) => { + const newLanguage = e.target.value + state.language = newLanguage + localStorage.setItem('wplace_language', newLanguage) + + setTimeout(() => { + settingsContainer.style.display = "none" + createUI() + }, 100) + }) + } + + const themeSelect = settingsContainer.querySelector("#themeSelect") + if (themeSelect) { + themeSelect.addEventListener("change", (e) => { + const newTheme = e.target.value + switchTheme(newTheme) + }) + } + + const overlayOpacitySlider = settingsContainer.querySelector("#overlayOpacitySlider"); + const overlayOpacityValue = settingsContainer.querySelector("#overlayOpacityValue"); + const enableBlueMarbleToggle = settingsContainer.querySelector("#enableBlueMarbleToggle"); + + if (overlayOpacitySlider && overlayOpacityValue) { + overlayOpacitySlider.addEventListener('input', (e) => { + const opacity = parseFloat(e.target.value); + state.overlayOpacity = opacity; + overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`; + }); + } + + // Speed slider event listener + const speedSlider = settingsContainer.querySelector("#speedSlider"); + const speedValue = settingsContainer.querySelector("#speedValue"); + if (speedSlider && speedValue) { + speedSlider.addEventListener('input', (e) => { + const speed = parseInt(e.target.value, 10); + state.paintingSpeed = speed; + speedValue.textContent = `${speed} (batch size)`; + saveBotSettings(); + }); + } + + if (enableBlueMarbleToggle) { + enableBlueMarbleToggle.addEventListener('click', async () => { + state.blueMarbleEnabled = enableBlueMarbleToggle.checked; + if (state.imageLoaded && overlayManager.imageBitmap) { + Utils.showAlert("Re-processing overlay...", "info"); + await overlayManager.processImageIntoChunks(); + Utils.showAlert("Overlay updated!", "success"); + } + }); + } + + // (Advanced color listeners moved outside to work with resize dialog) + // (Advanced color listeners moved outside to work with resize dialog) + // Notifications listeners + const notifPermBtn = settingsContainer.querySelector("#notifRequestPermBtn"); + const notifTestBtn = settingsContainer.querySelector("#notifTestBtn"); + if (notifPermBtn) { + notifPermBtn.addEventListener("click", async () => { + const perm = await NotificationManager.requestPermission(); + if (perm === "granted") Utils.showAlert("Notifications enabled.", "success"); + else Utils.showAlert("Notifications permission denied.", "warning"); + }); + } + if (notifTestBtn) { + notifTestBtn.addEventListener("click", () => { + NotificationManager.notify("WPlace — Test", "This is a test notification.", "wplace-notify-test", true); + }); + } + + } + + const widthSlider = resizeContainer.querySelector("#widthSlider") + const heightSlider = resizeContainer.querySelector("#heightSlider") + const widthValue = resizeContainer.querySelector("#widthValue") + const heightValue = resizeContainer.querySelector("#heightValue") + const keepAspect = resizeContainer.querySelector("#keepAspect") + const paintWhiteToggle = resizeContainer.querySelector("#paintWhiteToggle"); + const zoomSlider = resizeContainer.querySelector("#zoomSlider"); + const zoomValue = resizeContainer.querySelector('#zoomValue'); + const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); + const zoomOutBtn = resizeContainer.querySelector('#zoomOutBtn'); + const zoomFitBtn = resizeContainer.querySelector('#zoomFitBtn'); + const zoomActualBtn = resizeContainer.querySelector('#zoomActualBtn'); + const panModeBtn = resizeContainer.querySelector('#panModeBtn'); + const panStage = resizeContainer.querySelector('#resizePanStage'); + const canvasStack = resizeContainer.querySelector('#resizeCanvasStack'); + const baseCanvas = resizeContainer.querySelector('#resizeCanvas'); + const maskCanvas = resizeContainer.querySelector('#maskCanvas'); + const baseCtx = baseCanvas.getContext('2d'); + const maskCtx = maskCanvas.getContext('2d'); + const confirmResize = resizeContainer.querySelector("#confirmResize") + const cancelResize = resizeContainer.querySelector("#cancelResize") + const downloadPreviewBtn = resizeContainer.querySelector("#downloadPreviewBtn"); + const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn'); + + if (compactBtn) { + compactBtn.addEventListener("click", () => { + container.classList.toggle("wplace-compact") + const isCompact = container.classList.contains("wplace-compact") + + if (isCompact) { + compactBtn.innerHTML = '' + compactBtn.title = "Expand Mode" + } else { + compactBtn.innerHTML = '' + compactBtn.title = "Compact Mode" + } + }) + } + + if (minimizeBtn) { + minimizeBtn.addEventListener("click", () => { + state.minimized = !state.minimized + if (state.minimized) { + container.classList.add("wplace-minimized") + content.classList.add("wplace-hidden") + minimizeBtn.innerHTML = '' + minimizeBtn.title = "Restore" + } else { + container.classList.remove("wplace-minimized") + content.classList.remove("wplace-hidden") + minimizeBtn.innerHTML = '' + minimizeBtn.title = "Minimize" + } + saveBotSettings() + }) + } + + if (toggleOverlayBtn) { + toggleOverlayBtn.addEventListener('click', () => { + const isEnabled = overlayManager.toggle(); + toggleOverlayBtn.classList.toggle('active', isEnabled); + toggleOverlayBtn.setAttribute('aria-pressed', isEnabled ? 'true' : 'false'); + Utils.showAlert(`Overlay ${isEnabled ? 'enabled' : 'disabled'}.`, 'info'); + }); + } + + if (state.minimized) { + container.classList.add("wplace-minimized") + content.classList.add("wplace-hidden") + if (minimizeBtn) { + minimizeBtn.innerHTML = '' + minimizeBtn.title = "Restore" + } + } else { + container.classList.remove("wplace-minimized") + content.classList.remove("wplace-hidden") + if (minimizeBtn) { + minimizeBtn.innerHTML = '' + minimizeBtn.title = "Minimize" + } + } + + if (saveBtn) { + saveBtn.addEventListener("click", () => { + if (!state.imageLoaded) { + Utils.showAlert(Utils.t("missingRequirements"), "error") + return + } + + const success = Utils.saveProgress() + if (success) { + updateUI("autoSaved", "success") + Utils.showAlert(Utils.t("autoSaved"), "success") + } else { + Utils.showAlert("❌ Erro ao salvar progresso", "error") + } + }) + } + + if (loadBtn) { + loadBtn.addEventListener("click", () => { + // Check if initial setup is complete + if (!state.initialSetupComplete) { + Utils.showAlert("🔄 Please wait for the initial setup to complete before loading progress.", "warning"); + return; + } + + const savedData = Utils.loadProgress() + if (!savedData) { + updateUI("noSavedData", "warning") + Utils.showAlert(Utils.t("noSavedData"), "warning") + return + } + + const confirmLoad = confirm( + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels`, + ) + + if (confirmLoad) { + const success = Utils.restoreProgress(savedData) + if (success) { + updateUI("dataLoaded", "success") + Utils.showAlert(Utils.t("dataLoaded"), "success") + updateDataButtons() + + updateStats() + + // Restore overlay if image data was loaded from localStorage + Utils.restoreOverlayFromData().catch(error => { + console.error('Failed to restore overlay from localStorage:', error); + }); + + if (!state.colorsChecked) { + uploadBtn.disabled = false; + } else { + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + } + + if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { + startBtn.disabled = false + } + } else { + Utils.showAlert("❌ Erro ao carregar progresso", "error") + } + } + }) + } + + if (saveToFileBtn) { + saveToFileBtn.addEventListener("click", () => { + const success = Utils.saveProgressToFile() + if (success) { + updateUI("fileSaved", "success") + Utils.showAlert(Utils.t("fileSaved"), "success") + } else { + Utils.showAlert(Utils.t("fileError"), "error") + } + }) + } + + if (loadFromFileBtn) { + loadFromFileBtn.addEventListener("click", async () => { + // Check if initial setup is complete + if (!state.initialSetupComplete) { + Utils.showAlert("🔄 Please wait for the initial setup to complete before loading from file.", "warning"); + return; + } + + try { + const success = await Utils.loadProgressFromFile() + if (success) { + updateUI("fileLoaded", "success") + Utils.showAlert(Utils.t("fileLoaded"), "success") + updateDataButtons() + + await updateStats() + + // Restore overlay if image data was loaded from file + await Utils.restoreOverlayFromData().catch(error => { + console.error('Failed to restore overlay from file:', error); + }); + + if (state.colorsChecked) { + uploadBtn.disabled = false + selectPosBtn.disabled = false + resizeBtn.disabled = false + } else { + uploadBtn.disabled = false; + } + + if (state.imageLoaded && state.startPosition && state.region && state.colorsChecked) { + startBtn.disabled = false + } + } + } catch (error) { + if (error.message === "Invalid JSON file") { + Utils.showAlert(Utils.t("invalidFileFormat"), "error") + } else { + Utils.showAlert(Utils.t("fileError"), "error") + } + } + }) + } + + updateUI = (messageKey, type = "default", params = {}) => { + const message = Utils.t(messageKey, params) + statusText.textContent = message + statusText.className = `wplace-status status-${type}` + statusText.style.animation = "none" + void statusText.offsetWidth + statusText.style.animation = "slideIn 0.3s ease-out" + } + + updateStats = async () => { + localStorage.removeItem("lp"); + const { id, charges, cooldown, max, droplets } = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(charges); + state.cooldown = cooldown; + state.maxCharges = Math.floor(max) > 1 ? Math.floor(max) : state.maxCharges; + // Evaluate notifications every time we refresh server-side charges + NotificationManager.maybeNotifyChargesReached(); + + if (cooldownSlider.max != state.maxCharges) { + cooldownSlider.max = state.maxCharges; + } + + let imageStatsHTML = ''; + if (state.imageLoaded) { + const progress = state.totalPixels > 0 ? Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; + const remainingPixels = state.totalPixels - state.paintedPixels; + state.estimatedTime = Utils.calculateEstimatedTime(remainingPixels, state.currentCharges, state.cooldown); + if (progressBar) progressBar.style.width = `${progress}%`; + + imageStatsHTML = ` +
+
${Utils.t("progress")}
+
${progress}%
+
+
+
${Utils.t("pixels")}
+
${state.paintedPixels}/${state.totalPixels}
+
+
+
${Utils.t("estimatedTime")}
+
${Utils.formatTime(state.estimatedTime)}
+
+ `; + } + + let colorSwatchesHTML = ''; + if (state.colorsChecked) { + colorSwatchesHTML = state.availableColors.map(color => { + const rgbString = `rgb(${color.rgb.join(',')})`; + return `
`; + }).join(''); + } + + let totalAllCharges = 0; + if (state.allAccountsInfo.length > 0) { + totalAllCharges = state.allAccountsInfo.reduce((sum, acc) => sum + Math.floor(acc.Charges || 0), 0); + totalMaxCharges = state.allAccountsInfo.reduce((sum, acc) => sum + Math.floor(acc.Max || 0), 0); + } + + if (statsArea) statsArea.innerHTML = ` + ${imageStatsHTML} +
+
Total All Accounts Charges
+
${totalAllCharges}/${totalMaxCharges}
+
+
+
${Utils.t("charges")}
+
${Math.floor(state.currentCharges)} / ${state.maxCharges}
+
+ ${state.colorsChecked ? ` +
+
Available Colors (${state.availableColors.length})
+
+ ${colorSwatchesHTML} +
+
+ ` : ''} + `; + + // Sync with the accounts list + if (state.allAccountsInfo.length > 0 && id) { + const currentAccountInList = state.allAccountsInfo.find(acc => acc.ID === id); + if (currentAccountInList) { + currentAccountInList.Charges = state.currentCharges; + currentAccountInList.Max = state.maxCharges; + currentAccountInList.Droplets = droplets; + // Re-render the list to show the updated charge count + renderAccountsList(); + } + } + } + + updateDataButtons = () => { + const hasImageData = state.imageLoaded && state.imageData + saveBtn.disabled = !hasImageData + saveToFileBtn.disabled = !hasImageData + } + + updateDataButtons() + + function showResizeDialog(processor) { + let baseProcessor = processor; + let width, height; + if (state.originalImage?.dataUrl) { + baseProcessor = new ImageProcessor(state.originalImage.dataUrl); + width = state.originalImage.width; + height = state.originalImage.height; + } else { + const dims = processor.getDimensions(); + width = dims.width; + height = dims.height; + } + const aspectRatio = width / height; + + const rs = state.resizeSettings; + widthSlider.max = width * 2; + heightSlider.max = height * 2; + let initialW = width; + let initialH = height; + if (rs && Number.isFinite(rs.width) && Number.isFinite(rs.height) && rs.width > 0 && rs.height > 0) { + initialW = rs.width; + initialH = rs.height; + } + // Clamp to slider ranges + initialW = Math.max(parseInt(widthSlider.min, 10) || 10, Math.min(initialW, parseInt(widthSlider.max, 10))); + initialH = Math.max(parseInt(heightSlider.min, 10) || 10, Math.min(initialH, parseInt(heightSlider.max, 10))); + widthSlider.value = initialW; + heightSlider.value = initialH; + widthValue.textContent = initialW; + heightValue.textContent = initialH; + zoomSlider.value = 1; + if (zoomValue) zoomValue.textContent = '100%'; + paintWhiteToggle.checked = state.paintWhitePixels; + + let _previewTimer = null; + let _previewJobId = 0; + let _isDraggingSize = false; + let _zoomLevel = 1; + let _ditherWorkBuf = null; + let _ditherEligibleBuf = null; + const ensureDitherBuffers = (n) => { + if (!_ditherWorkBuf || _ditherWorkBuf.length !== n * 3) _ditherWorkBuf = new Float32Array(n * 3); + if (!_ditherEligibleBuf || _ditherEligibleBuf.length !== n) _ditherEligibleBuf = new Uint8Array(n); + return { work: _ditherWorkBuf, eligible: _ditherEligibleBuf }; + }; + let _maskImageData = null; + let _maskData = null; + let _dirty = null; + const _resetDirty = () => { _dirty = { minX: Infinity, minY: Infinity, maxX: -1, maxY: -1 }; }; + const _markDirty = (x, y) => { + if (!_dirty) _resetDirty(); + if (x < _dirty.minX) _dirty.minX = x; + if (y < _dirty.minY) _dirty.minY = y; + if (x > _dirty.maxX) _dirty.maxX = x; + if (y > _dirty.maxY) _dirty.maxY = y; + }; + const _flushDirty = () => { + if (!_dirty || _dirty.maxX < _dirty.minX || _dirty.maxY < _dirty.minY) return; + const x = Math.max(0, _dirty.minX); + const y = Math.max(0, _dirty.minY); + const w = Math.min(maskCanvas.width - x, _dirty.maxX - x + 1); + const h = Math.min(maskCanvas.height - y, _dirty.maxY - y + 1); + if (w > 0 && h > 0) maskCtx.putImageData(_maskImageData, 0, 0, x, y, w, h); + _resetDirty(); + }; + const _ensureMaskOverlayBuffers = (w, h, rebuildFromMask = false) => { + if (!_maskImageData || _maskImageData.width !== w || _maskImageData.height !== h) { + _maskImageData = maskCtx.createImageData(w, h); + _maskData = _maskImageData.data; + rebuildFromMask = true; + } + if (rebuildFromMask) { + const m = state.resizeIgnoreMask; + const md = _maskData; + md.fill(0); + if (m) { + for (let i = 0; i < m.length; i++) if (m[i]) { const p = i * 4; md[p] = 255; md[p + 1] = 0; md[p + 2] = 0; md[p + 3] = 150; } + } + maskCtx.putImageData(_maskImageData, 0, 0); + _resetDirty(); + } + }; + const ensureMaskSize = (w, h) => { + if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { + state.resizeIgnoreMask = new Uint8Array(w * h); + } + baseCanvas.width = w; baseCanvas.height = h; + maskCanvas.width = w; maskCanvas.height = h; + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + // Ensure overlay buffers exist and rebuild from mask when dimensions change + _ensureMaskOverlayBuffers(w, h, true); + }; + _updateResizePreview = async () => { + const jobId = ++_previewJobId; + const newWidth = parseInt(widthSlider.value, 10); + const newHeight = parseInt(heightSlider.value, 10); + _zoomLevel = parseFloat(zoomSlider.value); + + widthValue.textContent = newWidth; + heightValue.textContent = newHeight; + + ensureMaskSize(newWidth, newHeight); + canvasStack.style.width = newWidth + 'px'; + canvasStack.style.height = newHeight + 'px'; + baseCtx.imageSmoothingEnabled = false; + if (!state.availableColors || state.availableColors.length === 0) { + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + baseCtx.clearRect(0, 0, newWidth, newHeight); + baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + // Draw existing mask overlay buffer + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); + updateZoomLayout(); + return; + } + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + baseCtx.clearRect(0, 0, newWidth, newHeight); + baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + const imgData = baseCtx.getImageData(0, 0, newWidth, newHeight); + const data = imgData.data; + + const tThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + + const applyFSDither = () => { + const w = newWidth, h = newHeight; + const n = w * h; + const { work, eligible } = ensureDitherBuffers(n); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + const i4 = idx * 4; + const r = data[i4], g = data[i4 + 1], b = data[i4 + 2], a = data[i4 + 3]; + const isEligible = a >= tThresh && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + eligible[idx] = isEligible ? 1 : 0; + work[idx * 3] = r; + work[idx * 3 + 1] = g; + work[idx * 3 + 2] = b; + if (!isEligible) { + data[i4 + 3] = 0; // transparent in preview overlay + } + } + } + + const diffuse = (nx, ny, er, eg, eb, factor) => { + if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; + const nidx = ny * w + nx; + if (!eligible[nidx]) return; + const base = nidx * 3; + work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); + work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); + work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); + }; + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + if (!eligible[idx]) continue; + const base = idx * 3; + const r0 = work[base], g0 = work[base + 1], b0 = work[base + 2]; + const [nr, ng, nb] = Utils.findClosestPaletteColor(r0, g0, b0, state.activeColorPalette); + const i4 = idx * 4; + data[i4] = nr; + data[i4 + 1] = ng; + data[i4 + 2] = nb; + data[i4 + 3] = 255; + + const er = r0 - nr; + const eg = g0 - ng; + const eb = b0 - nb; + + diffuse(x + 1, y, er, eg, eb, 7 / 16); + diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); + diffuse(x, y + 1, er, eg, eb, 5 / 16); + diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); + } + } + }; + + // Skip expensive dithering while user is dragging sliders + if (state.ditheringEnabled && !_isDraggingSize) { + applyFSDither(); + } else { + for (let i = 0; i < data.length; i += 4) { + const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3]; + if (a < tThresh || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { + data[i + 3] = 0; + continue; + } + const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + data[i] = nr; + data[i + 1] = ng; + data[i + 2] = nb; + data[i + 3] = 255; + } + } + + if (jobId !== _previewJobId) return; + baseCtx.putImageData(imgData, 0, 0); + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); + updateZoomLayout(); + }; + + const onWidthInput = () => { + if (keepAspect.checked) { + heightSlider.value = Math.round(parseInt(widthSlider.value, 10) / aspectRatio); + } + _updateResizePreview(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; + saveBotSettings(); + // Auto-fit after size changes + const fit = (typeof computeFitZoom === 'function') ? computeFitZoom() : 1; + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + }; + + const onHeightInput = () => { + if (keepAspect.checked) { + widthSlider.value = Math.round(parseInt(heightSlider.value, 10) * aspectRatio); + } + _updateResizePreview(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; + saveBotSettings(); + // Auto-fit after size changes + const fit = (typeof computeFitZoom === 'function') ? computeFitZoom() : 1; + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + }; + + paintWhiteToggle.onchange = (e) => { + state.paintWhitePixels = e.target.checked; + _updateResizePreview(); + }; + + let panX = 0, panY = 0; + const clampPan = () => { + const wrapRect = panStage?.getBoundingClientRect() || { width: 0, height: 0 }; + const w = (baseCanvas.width || 1) * _zoomLevel; + const h = (baseCanvas.height || 1) * _zoomLevel; + if (w <= wrapRect.width) { + panX = Math.floor((wrapRect.width - w) / 2); + } else { + const minX = wrapRect.width - w; + panX = Math.min(0, Math.max(minX, panX)); + } + if (h <= wrapRect.height) { + panY = Math.floor((wrapRect.height - h) / 2); + } else { + const minY = wrapRect.height - h; + panY = Math.min(0, Math.max(minY, panY)); + } + }; + let _panRaf = 0; + const applyPan = () => { + if (_panRaf) return; + _panRaf = requestAnimationFrame(() => { + clampPan(); + canvasStack.style.transform = `translate3d(${Math.round(panX)}px, ${Math.round(panY)}px, 0) scale(${_zoomLevel})`; + _panRaf = 0; + }); + }; + + const updateZoomLayout = () => { + const w = baseCanvas.width || 1, h = baseCanvas.height || 1; + baseCanvas.style.width = w + 'px'; + baseCanvas.style.height = h + 'px'; + maskCanvas.style.width = w + 'px'; + maskCanvas.style.height = h + 'px'; + canvasStack.style.width = w + 'px'; + canvasStack.style.height = h + 'px'; + applyPan(); + }; + const applyZoom = (z) => { + _zoomLevel = Math.max(0.05, Math.min(20, z || 1)); + zoomSlider.value = _zoomLevel; + updateZoomLayout(); + if (zoomValue) zoomValue.textContent = `${Math.round(_zoomLevel * 100)}%`; + }; + zoomSlider.addEventListener('input', () => { + applyZoom(parseFloat(zoomSlider.value)); + }); + if (zoomInBtn) zoomInBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) + 0.1)); + if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) - 0.1)); + const computeFitZoom = () => { + const wrapRect = panStage?.getBoundingClientRect(); + if (!wrapRect) return 1; + const w = baseCanvas.width || 1; + const h = baseCanvas.height || 1; + const margin = 10; + const scaleX = (wrapRect.width - margin) / w; + const scaleY = (wrapRect.height - margin) / h; + return Math.max(0.05, Math.min(20, Math.min(scaleX, scaleY))); + }; + if (zoomFitBtn) zoomFitBtn.addEventListener('click', () => { applyZoom(computeFitZoom()); centerInView(); }); + if (zoomActualBtn) zoomActualBtn.addEventListener('click', () => { applyZoom(1); centerInView(); }); + + const centerInView = () => { + if (!panStage) return; + const rect = panStage.getBoundingClientRect(); + const w = (baseCanvas.width || 1) * _zoomLevel; + const h = (baseCanvas.height || 1) * _zoomLevel; + panX = Math.floor((rect.width - w) / 2); + panY = Math.floor((rect.height - h) / 2); + applyPan(); + }; + + let isPanning = false; let startX = 0, startY = 0, startPanX = 0, startPanY = 0; + let allowPan = false; // Space key + let panMode = false; // Explicit pan mode toggle for touch/one-button mice + const isPanMouseButton = (e) => e.button === 1 || e.button === 2; + const setCursor = (val) => { if (panStage) panStage.style.cursor = val; }; + const isPanActive = (e) => panMode || allowPan || isPanMouseButton(e); + const updatePanModeBtn = () => { + if (!panModeBtn) return; + panModeBtn.classList.toggle('active', panMode); + panModeBtn.setAttribute('aria-pressed', panMode ? 'true' : 'false'); + }; + if (panModeBtn) { + updatePanModeBtn(); + panModeBtn.addEventListener('click', () => { panMode = !panMode; updatePanModeBtn(); setCursor(panMode ? 'grab' : ''); }); + } + if (panStage) { + panStage.addEventListener('contextmenu', (e) => { if (allowPan) e.preventDefault(); }); + window.addEventListener('keydown', (e) => { if (e.code === 'Space') { allowPan = true; setCursor('grab'); } }); + window.addEventListener('keyup', (e) => { if (e.code === 'Space') { allowPan = false; if (!isPanning) setCursor(''); } }); + panStage.addEventListener('mousedown', (e) => { + if (!isPanActive(e)) return; + e.preventDefault(); + isPanning = true; startX = e.clientX; startY = e.clientY; startPanX = panX; startPanY = panY; + setCursor('grabbing'); + }); + window.addEventListener('mousemove', (e) => { + if (!isPanning) return; + const dx = e.clientX - startX; const dy = e.clientY - startY; + panX = startPanX + dx; panY = startPanY + dy; applyPan(); + }); + window.addEventListener('mouseup', () => { if (isPanning) { isPanning = false; setCursor(allowPan ? 'grab' : ''); } }); + panStage.addEventListener('wheel', (e) => { + if (!e.ctrlKey && !e.metaKey) return; + e.preventDefault(); + const rect = panStage.getBoundingClientRect(); + const cx = e.clientX - rect.left - panX; + const cy = e.clientY - rect.top - panY; + const before = _zoomLevel; + const step = Math.max(0.05, Math.min(0.5, Math.abs(e.deltaY) > 20 ? 0.2 : 0.1)); + const next = Math.max(0.05, Math.min(20, before + (e.deltaY > 0 ? -step : step))); + if (next === before) return; + const scale = next / before; + panX = panX - cx * (scale - 1); + panY = panY - cy * (scale - 1); + applyZoom(next); + }, { passive: false }); + let lastTouchDist = null; + let touchStartTime = 0; + let doubleTapTimer = null; + panStage.addEventListener('touchstart', (e) => { + if (e.touches.length === 1) { + const t = e.touches[0]; + isPanning = true; startX = t.clientX; startY = t.clientY; startPanX = panX; startPanY = panY; + setCursor('grabbing'); + const now = Date.now(); + if (now - touchStartTime < 300) { + // double tap -> toggle 100%/fit + const z = Math.abs(_zoomLevel - 1) < 0.01 ? computeFitZoom() : 1; + applyZoom(z); + centerInView(); + if (doubleTapTimer) clearTimeout(doubleTapTimer); + } else { + touchStartTime = now; + doubleTapTimer = setTimeout(() => { doubleTapTimer = null; }, 320); + } + } else if (e.touches.length === 2) { + // Pinch start + const [a, b] = e.touches; + lastTouchDist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); + } + }, { passive: true }); + panStage.addEventListener('touchmove', (e) => { + if (e.touches.length === 1 && isPanning) { + const t = e.touches[0]; + const dx = t.clientX - startX; const dy = t.clientY - startY; + panX = startPanX + dx; panY = startPanY + dy; applyPan(); + } else if (e.touches.length === 2 && lastTouchDist != null) { + e.preventDefault(); + const [a, b] = e.touches; + const dist = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY); + const rect = panStage.getBoundingClientRect(); + const centerX = (a.clientX + b.clientX) / 2 - rect.left - panX; + const centerY = (a.clientY + b.clientY) / 2 - rect.top - panY; + const before = _zoomLevel; + const scale = dist / (lastTouchDist || dist); + const next = Math.max(0.05, Math.min(20, before * scale)); + if (next !== before) { + panX = panX - centerX * (next / before - 1); + panY = panY - centerY * (next / before - 1); + applyZoom(next); + } + lastTouchDist = dist; + } + }, { passive: false }); + panStage.addEventListener('touchend', () => { + isPanning = false; lastTouchDist = null; setCursor(panMode || allowPan ? 'grab' : ''); + }); + } + const schedulePreview = () => { + if (_previewTimer) clearTimeout(_previewTimer); + const run = () => { + _previewTimer = null; + _updateResizePreview(); + }; + if (window.requestIdleCallback) { + _previewTimer = setTimeout(() => requestIdleCallback(run, { timeout: 150 }), 50); + } else { + _previewTimer = setTimeout(() => requestAnimationFrame(run), 50); + } + }; + // Track dragging to reduce work and skip dithering during drag + const markDragStart = () => { _isDraggingSize = true; }; + const markDragEnd = () => { _isDraggingSize = false; schedulePreview(); }; + widthSlider.addEventListener('pointerdown', markDragStart); + heightSlider.addEventListener('pointerdown', markDragStart); + widthSlider.addEventListener('pointerup', markDragEnd); + heightSlider.addEventListener('pointerup', markDragEnd); + widthSlider.addEventListener("input", () => { onWidthInput(); schedulePreview(); }); + heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); }); + + // Mask painting UX: brush size, modes, row/column fills, and precise coords + let draggingMask = false; + let lastPaintX = -1, lastPaintY = -1; + let brushSize = 1; + let rowColSize = 1; + let maskMode = 'ignore'; // 'ignore' | 'unignore' | 'toggle' + const brushEl = resizeContainer.querySelector('#maskBrushSize'); + const brushValEl = resizeContainer.querySelector('#maskBrushSizeValue'); + const btnIgnore = resizeContainer.querySelector('#maskModeIgnore'); + const btnUnignore = resizeContainer.querySelector('#maskModeUnignore'); + const btnToggle = resizeContainer.querySelector('#maskModeToggle'); + const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn'); + const invertMaskBtn = resizeContainer.querySelector('#invertMaskBtn'); + const rowColSizeEl = resizeContainer.querySelector('#rowColSize'); + const rowColSizeValEl = resizeContainer.querySelector('#rowColSizeValue'); + + const updateModeButtons = () => { + const map = [ + [btnIgnore, 'ignore'], + [btnUnignore, 'unignore'], + [btnToggle, 'toggle'] + ]; + for (const [el, m] of map) { + if (!el) continue; + const active = maskMode === m; + el.classList.toggle('active', active); + el.setAttribute('aria-pressed', active ? 'true' : 'false'); + } + }; + const setMode = (mode) => { maskMode = mode; updateModeButtons(); }; + if (brushEl && brushValEl) { + brushEl.addEventListener('input', () => { brushSize = parseInt(brushEl.value, 10) || 1; brushValEl.textContent = brushSize; }); + brushValEl.textContent = brushEl.value; + brushSize = parseInt(brushEl.value, 10) || 1; + } + if (rowColSizeEl && rowColSizeValEl) { + rowColSizeEl.addEventListener('input', () => { rowColSize = parseInt(rowColSizeEl.value, 10) || 1; rowColSizeValEl.textContent = rowColSize; }); + rowColSizeValEl.textContent = rowColSizeEl.value; + rowColSize = parseInt(rowColSizeEl.value, 10) || 1; + } + if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore')); + if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore')); + if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle')); + // Initialize button state (default to toggle mode) + updateModeButtons(); + + const mapClientToPixel = (clientX, clientY) => { + // Compute without rounding until final step to avoid drift at higher zoom + const rect = baseCanvas.getBoundingClientRect(); + const scaleX = rect.width / baseCanvas.width; + const scaleY = rect.height / baseCanvas.height; + const dx = (clientX - rect.left) / scaleX; + const dy = (clientY - rect.top) / scaleY; + const x = Math.floor(dx); + const y = Math.floor(dy); + return { x, y }; + }; + + const ensureMask = (w, h) => { + if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) { + state.resizeIgnoreMask = new Uint8Array(w * h); + } + }; + + const paintCircle = (cx, cy, radius, value) => { + const w = baseCanvas.width, h = baseCanvas.height; + ensureMask(w, h); + const r2 = radius * radius; + for (let yy = cy - radius; yy <= cy + radius; yy++) { + if (yy < 0 || yy >= h) continue; + for (let xx = cx - radius; xx <= cx + radius; xx++) { + if (xx < 0 || xx >= w) continue; + const dx = xx - cx, dy = yy - cy; + if (dx * dx + dy * dy <= r2) { + const idx = yy * w + xx; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { _maskData[p] = 255; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 150; } + else { _maskData[p] = 0; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 0; } + _markDirty(xx, yy); + } + } + } + } + }; + + const paintRow = (y, value) => { + const w = baseCanvas.width, h = baseCanvas.height; + ensureMask(w, h); + if (y < 0 || y >= h) return; + + // Paint multiple rows based on rowColSize + const halfSize = Math.floor(rowColSize / 2); + const startY = Math.max(0, y - halfSize); + const endY = Math.min(h - 1, y + halfSize); + + for (let rowY = startY; rowY <= endY; rowY++) { + for (let x = 0; x < w; x++) { + const idx = rowY * w + x; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { _maskData[p] = 255; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 150; } + else { _maskData[p] = 0; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 0; } + } + } + if (_maskData) { _markDirty(0, rowY); _markDirty(w - 1, rowY); } + } + }; + + const paintColumn = (x, value) => { + const w = baseCanvas.width, h = baseCanvas.height; + ensureMask(w, h); + if (x < 0 || x >= w) return; + + // Paint multiple columns based on rowColSize + const halfSize = Math.floor(rowColSize / 2); + const startX = Math.max(0, x - halfSize); + const endX = Math.min(w - 1, x + halfSize); + + for (let colX = startX; colX <= endX; colX++) { + for (let y = 0; y < h; y++) { + const idx = y * w + colX; + let val = state.resizeIgnoreMask[idx]; + if (maskMode === 'toggle') { + val = val ? 0 : 1; + } else if (maskMode === 'ignore') { + val = 1; + } else { + val = 0; + } + state.resizeIgnoreMask[idx] = val; + if (_maskData) { + const p = idx * 4; + if (val) { _maskData[p] = 255; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 150; } + else { _maskData[p] = 0; _maskData[p + 1] = 0; _maskData[p + 2] = 0; _maskData[p + 3] = 0; } + } + } + if (_maskData) { _markDirty(colX, 0); _markDirty(colX, h - 1); } + } + }; + + const redrawMaskOverlay = () => { + // Only flush the dirty region; full rebuild happens on size change + _flushDirty(); + }; + + const handlePaint = (e) => { + // Suppress painting while panning + if ((e.buttons & 4) === 4 || (e.buttons & 2) === 2 || allowPan) return; + const { x, y } = mapClientToPixel(e.clientX, e.clientY); + const w = baseCanvas.width, h = baseCanvas.height; + if (x < 0 || y < 0 || x >= w || y >= h) return; + const radius = Math.max(1, Math.floor(brushSize / 2)); + if (e.shiftKey) { + paintRow(y); + } else if (e.altKey) { + paintColumn(x); + } else { + paintCircle(x, y, radius); + } + lastPaintX = x; lastPaintY = y; + redrawMaskOverlay(); + }; + + maskCanvas.addEventListener('mousedown', (e) => { + if (e.button === 1 || e.button === 2 || allowPan) return; // let pan handler manage + draggingMask = true; handlePaint(e); + }); + // Avoid hijacking touch gestures for panning/zooming + maskCanvas.addEventListener('touchstart', (e) => { /* let panStage handle */ }, { passive: true }); + maskCanvas.addEventListener('touchmove', (e) => { /* let panStage handle */ }, { passive: true }); + maskCanvas.addEventListener('touchend', (e) => { /* let panStage handle */ }, { passive: true }); + window.addEventListener('mousemove', (e) => { if (draggingMask) handlePaint(e); }); + window.addEventListener('mouseup', () => { if (draggingMask) { draggingMask = false; saveBotSettings(); } }); + + if (clearIgnoredBtnEl) clearIgnoredBtnEl.addEventListener('click', () => { + const w = baseCanvas.width, h = baseCanvas.height; + if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0); + _ensureMaskOverlayBuffers(w, h, true); + _updateResizePreview(); + saveBotSettings(); + }); + + if (invertMaskBtn) invertMaskBtn.addEventListener('click', () => { + if (!state.resizeIgnoreMask) return; + for (let i = 0; i < state.resizeIgnoreMask.length; i++) state.resizeIgnoreMask[i] = state.resizeIgnoreMask[i] ? 0 : 1; + const w = baseCanvas.width, h = baseCanvas.height; + _ensureMaskOverlayBuffers(w, h, true); + _updateResizePreview(); + saveBotSettings(); + }); + + confirmResize.onclick = async () => { + const newWidth = parseInt(widthSlider.value, 10); + const newHeight = parseInt(heightSlider.value, 10); + + // Generate the final paletted image data + const tempCanvas = document.createElement('canvas'); + const tempCtx = tempCanvas.getContext('2d'); + tempCanvas.width = newWidth; + tempCanvas.height = newHeight; + tempCtx.imageSmoothingEnabled = false; + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + await baseProcessor.load(); + } + tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); + const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight); + const data = imgData.data; + const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + let totalValidPixels = 0; + const mask = (state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight) ? state.resizeIgnoreMask : null; + + const applyFSDitherFinal = async () => { + const w = newWidth, h = newHeight; + const n = w * h; + const { work, eligible } = ensureDitherBuffers(n); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + const i4 = idx * 4; + const r = data[i4], g = data[i4 + 1], b = data[i4 + 2], a = data[i4 + 3]; + const masked = mask && mask[idx]; + const isEligible = !masked && a >= tThresh2 && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + eligible[idx] = isEligible ? 1 : 0; + work[idx * 3] = r; + work[idx * 3 + 1] = g; + work[idx * 3 + 2] = b; + if (!isEligible) { + data[i4 + 3] = 0; + } + } + // Yield to keep UI responsive + if ((y & 15) === 0) await Promise.resolve(); + } + + const diffuse = (nx, ny, er, eg, eb, factor) => { + if (nx < 0 || nx >= w || ny < 0 || ny >= h) return; + const nidx = ny * w + nx; + if (!eligible[nidx]) return; + const base = nidx * 3; + work[base] = Math.min(255, Math.max(0, work[base] + er * factor)); + work[base + 1] = Math.min(255, Math.max(0, work[base + 1] + eg * factor)); + work[base + 2] = Math.min(255, Math.max(0, work[base + 2] + eb * factor)); + }; + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const idx = y * w + x; + if (!eligible[idx]) continue; + const base = idx * 3; + const r0 = work[base], g0 = work[base + 1], b0 = work[base + 2]; + const [nr, ng, nb] = Utils.findClosestPaletteColor(r0, g0, b0, state.activeColorPalette); + const i4 = idx * 4; + data[i4] = nr; + data[i4 + 1] = ng; + data[i4 + 2] = nb; + data[i4 + 3] = 255; + totalValidPixels++; + + const er = r0 - nr; + const eg = g0 - ng; + const eb = b0 - nb; + + diffuse(x + 1, y, er, eg, eb, 7 / 16); + diffuse(x - 1, y + 1, er, eg, eb, 3 / 16); + diffuse(x, y + 1, er, eg, eb, 5 / 16); + diffuse(x + 1, y + 1, er, eg, eb, 1 / 16); + } + // Yield every row to reduce jank + await Promise.resolve(); + } + }; + + if (state.ditheringEnabled) { + await applyFSDitherFinal(); + } else { + for (let i = 0; i < data.length; i += 4) { + const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3]; + const masked = mask && mask[(i >> 2)]; + const isTransparent = a < tThresh2 || masked; + const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(r, g, b); + if (isTransparent || isWhiteAndSkipped) { + data[i + 3] = 0; // overlay transparency + continue; + } + totalValidPixels++; + const [nr, ng, nb] = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + data[i] = nr; + data[i + 1] = ng; + data[i + 2] = nb; + data[i + 3] = 255; + } + } + tempCtx.putImageData(imgData, 0, 0); + + // Save the final pixel data for painting + // Persist the paletted (and possibly dithered) pixels so painting uses the same output seen in overlay + const palettedPixels = new Uint8ClampedArray(imgData.data); + state.imageData.pixels = palettedPixels; + state.imageData.width = newWidth; + state.imageData.height = newHeight; + state.imageData.totalPixels = totalValidPixels; + state.totalPixels = totalValidPixels; + state.paintedPixels = 0; + + state.resizeSettings = { baseWidth: width, baseHeight: height, width: newWidth, height: newHeight }; + saveBotSettings(); + + const finalImageBitmap = await createImageBitmap(tempCanvas); + await overlayManager.setImage(finalImageBitmap); + overlayManager.enable(); + toggleOverlayBtn.classList.add('active'); + toggleOverlayBtn.setAttribute('aria-pressed', 'true'); + + // Keep state.imageData.processor as the original-based source; painting uses paletted pixels already stored + + updateStats(); + updateUI("resizeSuccess", "success", { width: newWidth, height: newHeight }); + closeResizeDialog(); + }; + + downloadPreviewBtn.onclick = () => { + try { + const w = baseCanvas.width, h = baseCanvas.height; + const out = document.createElement('canvas'); + out.width = w; out.height = h; + const octx = out.getContext('2d'); + octx.imageSmoothingEnabled = false; + octx.drawImage(baseCanvas, 0, 0); + octx.drawImage(maskCanvas, 0, 0); + const link = document.createElement('a'); + link.download = 'wplace-preview.png'; + link.href = out.toDataURL(); + link.click(); + } catch (e) { console.warn('Failed to download preview:', e); } + }; + + cancelResize.onclick = closeResizeDialog; + + resizeOverlay.style.display = "block"; + resizeContainer.style.display = "block"; + + // Reinitialize color palette with current available colors + initializeColorPalette(resizeContainer); + + _updateResizePreview(); + _resizeDialogCleanup = () => { + try { zoomSlider.replaceWith(zoomSlider.cloneNode(true)); } catch { } + try { if (zoomInBtn) zoomInBtn.replaceWith(zoomInBtn.cloneNode(true)); } catch { } + try { if (zoomOutBtn) zoomOutBtn.replaceWith(zoomOutBtn.cloneNode(true)); } catch { } + }; + setTimeout(() => { + if (typeof computeFitZoom === 'function') { + const z = computeFitZoom(); + if (!isNaN(z) && isFinite(z)) { + applyZoom(z); + centerInView(); + } + } else { + centerInView(); + } + }, 0); + } + + function closeResizeDialog() { + try { if (typeof _resizeDialogCleanup === 'function') { _resizeDialogCleanup(); } } catch { } + resizeOverlay.style.display = "none"; + resizeContainer.style.display = "none"; + _updateResizePreview = () => { }; + try { if (typeof cancelAnimationFrame === 'function' && _panRaf) { cancelAnimationFrame(_panRaf); } } catch { } + try { if (_previewTimer) { clearTimeout(_previewTimer); _previewTimer = null; } } catch { } + _maskImageData = null; _maskData = null; _dirty = null; + _ditherWorkBuf = null; _ditherEligibleBuf = null; + _resizeDialogCleanup = null; + } + + if (uploadBtn) { + uploadBtn.addEventListener("click", async () => { + const availableColors = Utils.extractAvailableColors(); + if (availableColors.length < 10) { + updateUI("noColorsFound", "error"); + Utils.showAlert(Utils.t("noColorsFound"), "error"); + return; + } + + if (!state.colorsChecked) { + state.availableColors = availableColors; + state.colorsChecked = true; + updateUI("colorsFound", "success", { count: availableColors.length }); + updateStats(); + selectPosBtn.disabled = false; + // Only enable resize button if image is also loaded + if (state.imageLoaded) { + resizeBtn.disabled = false; + } + } + + try { + updateUI("loadingImage", "default") + const imageSrc = await Utils.createImageUploader() + if (!imageSrc) { + updateUI("colorsFound", "success", { count: state.availableColors.length }); + return; + } + + const processor = new ImageProcessor(imageSrc) + await processor.load() + + const { width, height } = processor.getDimensions() + const pixels = processor.getPixelData() + + let totalValidPixels = 0; + for (let i = 0; i < pixels.length; i += 4) { + const isTransparent = pixels[i + 3] < (state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD); + const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(pixels[i], pixels[i + 1], pixels[i + 2]); + if (!isTransparent && !isWhiteAndSkipped) { + totalValidPixels++; + } + } + + state.imageData = { + width, + height, + pixels, + totalPixels: totalValidPixels, + processor, + } + + state.totalPixels = totalValidPixels + state.paintedPixels = 0 + state.imageLoaded = true + state.lastPosition = { x: 0, y: 0 } + + // Initialize painted map for tracking + Utils.initializePaintedMap(width, height); + + // New image: clear previous resize settings + state.resizeSettings = null; + // Also clear any previous ignore mask + state.resizeIgnoreMask = null; + // Save original image for this browser (dataUrl + dims) + state.originalImage = { dataUrl: imageSrc, width, height }; + saveBotSettings(); + + // Use the original image for the overlay initially + const imageBitmap = await createImageBitmap(processor.img); + await overlayManager.setImage(imageBitmap); + overlayManager.enable(); + toggleOverlayBtn.disabled = false; + toggleOverlayBtn.classList.add('active'); + toggleOverlayBtn.setAttribute('aria-pressed', 'true'); + + // Only enable resize button if colors have also been captured + if (state.colorsChecked) { + resizeBtn.disabled = false; + } + saveBtn.disabled = false + + if (state.startPosition) { + startBtn.disabled = false + } + + updateStats() + updateDataButtons() + updateUI("imageLoaded", "success", { count: totalValidPixels }) + } catch { + updateUI("imageError", "error") + } + }) + } + + if (resizeBtn) { + resizeBtn.addEventListener("click", () => { + if (state.imageLoaded && state.imageData.processor && state.colorsChecked) { + showResizeDialog(state.imageData.processor) + } else if (!state.colorsChecked) { + Utils.showAlert("Please upload an image first to capture available colors", "warning") + } + }) + } + + if (selectPosBtn) { + selectPosBtn.addEventListener("click", async () => { + if (state.selectingPosition) return + + state.selectingPosition = true + state.startPosition = null + state.region = null + startBtn.disabled = true + + Utils.showAlert(Utils.t("selectPositionAlert"), "info") + updateUI("waitingPosition", "default") + + const tempFetch = async (url, options) => { + if ( + typeof url === "string" && + url.includes("https://backend.wplace.live/s0/pixel/") && + options?.method?.toUpperCase() === "POST" + ) { + try { + const response = await originalFetch(url, options) + const clonedResponse = response.clone() + const data = await clonedResponse.json() + + if (data?.painted === 1) { + const regionMatch = url.match(/\/pixel\/(\d+)\/(\d+)/) + if (regionMatch && regionMatch.length >= 3) { + state.region = { + x: Number.parseInt(regionMatch[1]), + y: Number.parseInt(regionMatch[2]), + } + } + + const payload = JSON.parse(options.body) + if (payload?.coords && Array.isArray(payload.coords)) { + state.startPosition = { + x: payload.coords[0], + y: payload.coords[1], + } + state.lastPosition = { x: 0, y: 0 } + + await overlayManager.setPosition(state.startPosition, state.region); + + if (state.imageLoaded) { + startBtn.disabled = false + } + + window.fetch = originalFetch + state.selectingPosition = false + updateUI("positionSet", "success") + } + } + + return response + } catch { + return originalFetch(url, options) + } + } + return originalFetch(url, options) + } + + const originalFetch = window.fetch; + window.fetch = tempFetch; + + setTimeout(() => { + if (state.selectingPosition) { + window.fetch = originalFetch + state.selectingPosition = false + updateUI("positionTimeout", "error") + Utils.showAlert(Utils.t("positionTimeout"), "error") + } + }, 120000) + }) + } + + async function startPainting() { + if (!state.imageLoaded || !state.startPosition || !state.region) { + updateUI("missingRequirements", "error") + return false + } + await ensureToken() + if (!turnstileToken) return false + + state.running = true + state.stopFlag = false + startBtn.disabled = true + stopBtn.disabled = false + uploadBtn.disabled = true + selectPosBtn.disabled = true + resizeBtn.disabled = true + saveBtn.disabled = true + toggleOverlayBtn.disabled = true; + + updateUI("startPaintingMsg", "success") + + try { + await getAccounts(); + await processImage(); + return true + } catch { + updateUI("paintingError", "error") + return false + } finally { + state.running = false + stopBtn.disabled = true + saveBtn.disabled = false + + if (!state.stopFlag) { + startBtn.disabled = true + uploadBtn.disabled = false + selectPosBtn.disabled = false + resizeBtn.disabled = false + } else { + startBtn.disabled = false + } + toggleOverlayBtn.disabled = false; + } + } + + if (startBtn) { + startBtn.addEventListener("click", startPainting) + } + + if (stopBtn) { + stopBtn.addEventListener("click", () => { + state.stopFlag = true + state.running = false + stopBtn.disabled = true + updateUI("paintingStopped", "warning") + + if (state.imageLoaded && state.paintedPixels > 0) { + Utils.saveProgress() + Utils.showAlert(Utils.t("autoSaved"), "success") + } + }) + } + + const checkSavedProgress = () => { + const savedData = Utils.loadProgress() + if (savedData && savedData.state.paintedPixels > 0) { + const savedDate = new Date(savedData.timestamp).toLocaleString() + const progress = Math.round((savedData.state.paintedPixels / savedData.state.totalPixels) * 100) + + Utils.showAlert( + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${savedDate}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + + `${Utils.t("clickLoadToContinue")}`, + "info", + ) + } + } + + setTimeout(checkSavedProgress, 1000) + + if (cooldownSlider && cooldownValue) { + cooldownSlider.addEventListener("input", (e) => { + const threshold = parseInt(e.target.value); + state.cooldownChargeThreshold = threshold; + cooldownValue.textContent = threshold; + saveBotSettings(); + NotificationManager.resetEdgeTracking(); // prevent spurious notify after threshold change + }); + } + + loadBotSettings(); + // Ensure notification poller reflects current settings + NotificationManager.syncFromState(); + } + + async function processImage() { + const { width, height, pixels } = state.imageData + const { x: startX, y: startY } = state.startPosition + const { x: regionX, y: regionY } = state.region + + const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + const isEligibleAt = (x, y) => { + const idx = (y * width + x) * 4; + const r = pixels[idx], g = pixels[idx + 1], b = pixels[idx + 2], a = pixels[idx + 3]; + if (a < tThresh2) return false; + if (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) return false; + return true; + }; + + let startRow = 0; + let startCol = 0; + let foundStart = false; + let seen = 0; + const target = Math.max(0, Math.min(state.paintedPixels || 0, width * height)); + for (let y = 0; y < height && !foundStart; y++) { + for (let x = 0; x < width; x++) { + if (!isEligibleAt(x, y)) continue; + if (seen === target) { startRow = y; startCol = x; foundStart = true; break; } + seen++; + } + } + if (!foundStart) { startRow = height; startCol = 0; } + + let pixelBatch = null; + let skippedPixels = { transparent: 0, white: 0, alreadyPainted: 0 }; + + try { + outerLoop: for (let y = startRow; y < height; y++) { + for (let x = y === startRow ? startCol : 0; x < width; x++) { + if (state.stopFlag) { + if (pixelBatch && pixelBatch.pixels.length > 0) { + console.log(`🎯 Sending final batch before stop with ${pixelBatch.pixels.length} pixels`); + const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); + if (success) { + pixelBatch.pixels.forEach(() => { state.paintedPixels++; }); + state.currentCharges -= pixelBatch.pixels.length; + updateStats(); + } + } + state.lastPosition = { x, y } + updateUI("paintingPaused", "warning", { x, y }) + break outerLoop + } + + + const idx = (y * width + x) * 4 + const r = pixels[idx] + const g = pixels[idx + 1] + const b = pixels[idx + 2] + const alpha = pixels[idx + 3] + + const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + if (alpha < tThresh2 || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { + if (alpha < tThresh2) { + skippedPixels.transparent++; + } else { + skippedPixels.white++; + } + continue; + } + + let targetRgb; + if (Utils.isWhitePixel(r, g, b)) { + targetRgb = [255, 255, 255]; + } else { + targetRgb = Utils.findClosestPaletteColor(r, g, b, state.activeColorPalette); + } + + const colorId = findClosestColor([r, g, b], state.availableColors); + + let absX = startX + x; + let absY = startY + y; + + let adderX = Math.floor(absX / 1000); + let adderY = Math.floor(absY / 1000); + let pixelX = absX % 1000; + let pixelY = absY % 1000; + + if (!pixelBatch || + pixelBatch.regionX !== regionX + adderX || + pixelBatch.regionY !== regionY + adderY) { + + if (pixelBatch && pixelBatch.pixels.length > 0) { + console.log(`🌍 Sending region-change batch with ${pixelBatch.pixels.length} pixels (switching to region ${regionX + adderX},${regionY + adderY})`); + const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); + + if (success) { + pixelBatch.pixels.forEach((p) => { + state.paintedPixels++; + // Mark pixel as painted in map + Utils.markPixelPainted(p.x, p.y, pixelBatch.regionX, pixelBatch.regionY); + }); + state.currentCharges -= pixelBatch.pixels.length; + updateUI("paintingProgress", "default", { + painted: state.paintedPixels, + total: state.totalPixels, + }) + + // Use smart save instead of fixed interval + Utils.performSmartSave(); + + if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && pixelBatch.pixels.length > 0) { + // paintingSpeed now represents batch size, so add a small delay based on batch size + const batchDelayFactor = Math.max(1, 100 / state.paintingSpeed); // Larger batches = less delay + const totalDelay = Math.max(100, batchDelayFactor * pixelBatch.pixels.length); + await Utils.sleep(totalDelay) + } + updateStats(); + } else { + // If batch failed after all retries, stop painting to prevent infinite loops + console.error(`❌ Batch failed permanently after retries. Stopping painting.`); + state.stopFlag = true; + break outerLoop; + } + } + + pixelBatch = { + regionX: regionX + adderX, + regionY: regionY + adderY, + pixels: [] + }; + } + + + try { + const tileRegionX = pixelBatch ? (pixelBatch.regionX) : (regionX + adderX); + const tileRegionY = pixelBatch ? (pixelBatch.regionY) : (regionY + adderY); + const tileKeyParts = [(regionX + adderX), (regionY + adderY)]; + const existingColorRGBA = await overlayManager.getTilePixelColor(tileKeyParts[0], tileKeyParts[1], pixelX, pixelY).catch(() => null); + if (existingColorRGBA && Array.isArray(existingColorRGBA)) { + const [er, eg, eb] = existingColorRGBA; + const existingColorId = findClosestColor([er, eg, eb], state.availableColors); + // console.log(`pixel at (${pixelX}, ${pixelY}) has color ${existingColorId} it should be ${colorId}`); + if (existingColorId === colorId) { + skippedPixels.alreadyPainted++; + console.log(`Skipped already painted pixel at (${pixelX}, ${pixelY})`); + continue; // Skip + } + } + } catch (e) { + /* ignore */ + } + + pixelBatch.pixels.push({ + x: pixelX, + y: pixelY, + color: colorId, + localX: x, + localY: y, + }); + + // Calculate batch size based on mode (normal/random) + const maxBatchSize = calculateBatchSize(); + if (pixelBatch.pixels.length >= maxBatchSize) { + const modeText = state.batchMode === 'random' ? `random (${state.randomBatchMin}-${state.randomBatchMax})` : 'normal'; + console.log(`📦 Sending batch with ${pixelBatch.pixels.length} pixels (mode: ${modeText}, target: ${maxBatchSize})`); + const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); + + if (success) { + pixelBatch.pixels.forEach((pixel) => { + state.paintedPixels++; + // Mark pixel as painted in map + Utils.markPixelPainted(pixel.x, pixel.y, pixelBatch.regionX, pixelBatch.regionY); + }) + + state.currentCharges -= pixelBatch.pixels.length; + updateStats() + updateUI("paintingProgress", "default", { + painted: state.paintedPixels, + total: state.totalPixels, + }) + + // Use smart save instead of fixed interval + Utils.performSmartSave(); + + if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && pixelBatch.pixels.length > 0) { + const delayPerPixel = 1000 / state.paintingSpeed // ms per pixel + const totalDelay = Math.max(100, delayPerPixel * pixelBatch.pixels.length) // minimum 100ms + await Utils.sleep(totalDelay) + } + } else { + // If batch failed after all retries, stop painting to prevent infinite loops + console.error(`❌ Batch failed permanently after retries. Stopping painting.`); + state.stopFlag = true; + break outerLoop; + } + + pixelBatch.pixels = []; + } + if (!CONFIG.autoSwap) { + while (state.currentCharges < state.cooldownChargeThreshold && !state.stopFlag) { + const { charges, cooldown } = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(charges); + state.cooldown = cooldown; + + if (state.currentCharges >= state.cooldownChargeThreshold) { + // Edge-trigger a notification the instant threshold is crossed + NotificationManager.maybeNotifyChargesReached(true); + updateStats(); + break; + } + + // Enable save button during cooldown wait + saveBtn.disabled = false; + + updateUI("noChargesThreshold", "warning", { + time: Utils.formatTime(state.cooldown), + threshold: state.cooldownChargeThreshold, + current: state.currentCharges + }); + await updateStats(); + + // Allow auto save during cooldown + Utils.performSmartSave(); + + await Utils.sleep(state.cooldown); + } + } + else { + if (state.currentCharges < state.cooldownChargeThreshold && !state.stopFlag) { + console.log("⚠️ Charges too low, swapping to next account..."); + + const accounts = JSON.parse(localStorage.getItem("accounts")) || []; + if (accounts.length === 0) { + console.warn("❌ No accounts available, stopping painting."); + state.stopFlag = true; + return; + } + + state.accountIndex = (state.accountIndex + 1) % accounts.length; + console.log("🔄 Switching to account index:", state.accountIndex); + + const nextToken = accounts[state.accountIndex]; + console.log("🔑 Next token:", nextToken); + + if (!nextToken) { + console.warn("⚠️ Invalid token, skipping..."); + return; + } + + swapAccountTrigger(nextToken); + + let maxRetries = 20; + let retryCount = 0; + let swapSuccess = false; + + while (!swapSuccess && retryCount < maxRetries) { + console.log(`⏳ Waiting for account swap... (Attempt ${retryCount + 1}/${maxRetries})`); + + // Wait for a short period before checking. + await new Promise(resolve => setTimeout(resolve, 1000)); + + try { + await fetchAccount(); + + console.log("✅ Account swap confirmed."); + swapSuccess = true; + } catch (error) { + console.warn("❌ Account swap not yet successful. Retrying...", error); + retryCount++; + } + } + + if (swapSuccess) { + + const { charges, cooldown } = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(charges); + state.cooldown = cooldown; + Utils.performSmartSave(); + updateStats(); + } else { + console.error("❌ Failed to swap account after multiple retries. Stopping loop."); + state.stopFlag = true; + } + } + } + + // Disable save button again after exiting wait loop (back to normal painting) + if (!state.stopFlag) { + saveBtn.disabled = true; + } + if (state.stopFlag) break outerLoop; + + } + } + + if (pixelBatch && pixelBatch.pixels.length > 0 && !state.stopFlag) { + console.log(`🏁 Sending final batch with ${pixelBatch.pixels.length} pixels`); + const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); + if (success) { + pixelBatch.pixels.forEach((pixel) => { + state.paintedPixels++; + // Mark pixel as painted in map + Utils.markPixelPainted(pixel.x, pixel.y, pixelBatch.regionX, pixelBatch.regionY); + }) + state.currentCharges -= pixelBatch.pixels.length; + // Final save with painted map + Utils.saveProgress(); + + if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && pixelBatch.pixels.length > 0) { + const delayPerPixel = 1000 / state.paintingSpeed // ms per pixel + const totalDelay = Math.max(100, delayPerPixel * pixelBatch.pixels.length) // minimum 100ms + await Utils.sleep(totalDelay) + } + } else { + // If final batch failed after retries, log it + console.warn(`⚠️ Final batch failed with ${pixelBatch.pixels.length} pixels after all retries.`); + } + } + } finally { + if (window._chargesInterval) clearInterval(window._chargesInterval) + window._chargesInterval = null + } + + if (state.stopFlag) { + updateUI("paintingStopped", "warning") + // Save progress when stopped to preserve painted map + Utils.saveProgress() + } else { + updateUI("paintingComplete", "success", { count: state.paintedPixels }) + state.lastPosition = { x: 0, y: 0 } + // Keep painted map until user starts new project + // state.paintedMap = null // Commented out to preserve data + Utils.saveProgress() // Save final complete state + overlayManager.clear(); + const toggleOverlayBtn = document.getElementById('toggleOverlayBtn'); + if (toggleOverlayBtn) { + toggleOverlayBtn.classList.remove('active'); + toggleOverlayBtn.disabled = true; + } + } + + // Log skip statistics + console.log(`📊 Pixel Statistics:`); + console.log(` Painted: ${state.paintedPixels}`); + console.log(` Skipped - Transparent: ${skippedPixels.transparent}`); + console.log(` Skipped - White (disabled): ${skippedPixels.white}`); + console.log(` Skipped - Already painted: ${skippedPixels.alreadyPainted}`); + console.log(` Total processed: ${state.paintedPixels + skippedPixels.transparent + skippedPixels.white + skippedPixels.alreadyPainted}`); + + updateStats() + } + + const loadAccountNames = () => { + try { + return JSON.parse(localStorage.getItem("wplace-account-names")) || {}; + } catch (e) { + return {}; + } + }; + + const saveAccountNames = (names) => { + try { + localStorage.setItem("wplace-account-names", JSON.stringify(names)); + } catch (e) { + console.warn("Could not save account names:", e); + } + }; + + async function editAccountName(token, currentName) { + const newName = prompt("Enter a new name for this account:", currentName); + if (newName === null) return; // User cancelled + + if (newName.trim() === "") { + Utils.showAlert("Account name cannot be empty.", "warning"); + return; + } + + const names = loadAccountNames(); + names[token] = newName.trim(); + saveAccountNames(names); + + const accountInfo = state.allAccountsInfo.find(acc => acc.token === token); + if (accountInfo) { + accountInfo.displayName = newName.trim(); + } + + renderAccountsList(); + Utils.showAlert("Account name updated.", "success"); + } + + async function deleteAccount(token, name) { + if (!confirm(`Are you sure you want to delete the account "${name}"?\nThis action cannot be undone.`)) { + return; + } + + let accounts = JSON.parse(localStorage.getItem("accounts")) || []; + const initialCount = accounts.length; + accounts = accounts.filter(t => t !== token); + if (accounts.length < initialCount) { + localStorage.setItem("accounts", JSON.stringify(accounts)); + } + + const names = loadAccountNames(); + delete names[token]; + saveAccountNames(names); + + state.allAccountsInfo = state.allAccountsInfo.filter(acc => acc.token !== token); + renderAccountsList(); + Utils.showAlert(`Account "${name}" has been deleted.`, "success"); + } + + function renderAccountsList() { + const accountsListArea = document.getElementById('accountsListArea'); + if (!accountsListArea) return; + + accountsListArea.innerHTML = ''; + if (state.allAccountsInfo.length === 0) { + accountsListArea.innerHTML = `
No account data. Click to refresh.
`; + return; + } + + const theme = getCurrentTheme(); + state.allAccountsInfo.forEach((info, index) => { + const item = Utils.createElement('div', { + className: `wplace-account-item ${info.isCurrent ? 'is-current' : ''}` + }); + + const displayName = info.displayName || `Account ${index + 1}`; // this line is fine + + const details = Utils.createElement('div', { className: 'wplace-account-details' }); + details.appendChild(Utils.createElement('div', { className: 'wplace-account-name', title: displayName }, displayName)); + // details.appendChild(Utils.createElement('div', { className: 'wplace-account-id' }, `ID: ${info.ID}`)); + + let stats; + if (info.error) { + stats = Utils.createElement('div', { className: 'wplace-account-stats', style: { color: theme.error } }, 'Error'); + } else { + stats = Utils.createElement('div', { + className: 'wplace-account-stats', + innerHTML: ` + ${Math.floor(info.Charges || 0)}/${Math.floor(info.Max || 0)} + ${Math.floor(info.Droplets || 0)} + ` + }); + } + + const controls = Utils.createElement('div', { className: 'wplace-account-controls' }); + const editBtn = Utils.createElement('button', { + className: 'wplace-account-btn edit-btn', + title: 'Edit Name', + innerHTML: '' + }); + editBtn.addEventListener('click', (e) => { + e.stopPropagation(); + editAccountName(info.token, displayName); + }); + + const deleteBtn = Utils.createElement('button', { + className: 'wplace-account-btn delete-btn', + title: 'Delete Account', + innerHTML: '' + }); + deleteBtn.addEventListener('click', (e) => { + e.stopPropagation(); + deleteAccount(info.token, displayName); + }); + + controls.appendChild(editBtn); + controls.appendChild(deleteBtn); + + item.appendChild(details); + item.appendChild(stats); + item.appendChild(controls); + + accountsListArea.appendChild(item); + }); + } + + async function fetchAllAccountDetails() { + if (state.isFetchingAllAccounts) { + Utils.showAlert("Already fetching account details.", "warning"); + return; + } + state.isFetchingAllAccounts = true; + + const refreshBtn = document.getElementById('refreshAllAccountsBtn'); + if (refreshBtn) { + refreshBtn.innerHTML = ''; + refreshBtn.disabled = true; + } + + const accountsListArea = document.getElementById('accountsListArea'); + if (accountsListArea) { + accountsListArea.innerHTML = `
Initializing...
`; + } + + let originalToken = null; + + try { + await getAccounts(); + const accountsTokens = JSON.parse(localStorage.getItem("accounts")) || []; + if (accountsTokens.length === 0) { + if (accountsListArea) accountsListArea.innerHTML = `
No accounts found.
`; + return; + } + + const { id: originalId } = await WPlaceService.getCharges(); + state.allAccountsInfo = []; + renderAccountsList(); + + const accountNames = loadAccountNames(); + for (let i = 0; i < accountsTokens.length; i++) { + const token = accountsTokens[i]; + swapAccountTrigger(token); + + let retries = 0; + let swapped = false; + let fetchedInfo = null; + while (retries < 5 && !swapped) { + await Utils.sleep(1000); + try { + fetchedInfo = await WPlaceService.fetchCheck(); + if (fetchedInfo.ID) swapped = true; + } catch (e) { retries++; } + } + + if (swapped) { + await fetchAccount(); + // await purchase("max_charges"); + const displayName = accountNames[token] || `Account ${i + 1}`; + if (fetchedInfo.ID === originalId) originalToken = token; + state.allAccountsInfo.push({ ...fetchedInfo, token, displayName, isCurrent: fetchedInfo.ID === originalId }); + } else { + const displayName = accountNames[token] || `Account ${i + 1}`; + state.allAccountsInfo.push({ token, ID: `...${token.slice(-4)}`, displayName, error: 'Failed to fetch' }); + } + renderAccountsList(); + } + } catch (error) { + console.error("Error fetching all account details:", error); + if (accountsListArea) accountsListArea.innerHTML = `
Error loading accounts.
`; + } finally { + if (originalToken) swapAccountTrigger(originalToken); + await Utils.sleep(1000); + + // After switching back, update stats and sync the list + const meData = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(meData.charges); + state.cooldown = meData.cooldown; + state.maxCharges = Math.floor(meData.max) > 1 ? Math.floor(meData.max) : state.maxCharges; + const currentAccountInList = state.allAccountsInfo.find(acc => acc.ID === meData.id); + if (currentAccountInList) { + currentAccountInList.Charges = state.currentCharges; + currentAccountInList.Max = state.maxCharges; + currentAccountInList.Droplets = meData.droplets; + } + await updateStats(); // This will re-render the main stats + renderAccountsList(); // This will re-render the list with synced data + + state.isFetchingAllAccounts = false; + if (refreshBtn) { + refreshBtn.innerHTML = ''; + refreshBtn.disabled = false; + } + } + } + + // Helper function to calculate batch size based on mode + function calculateBatchSize() { + let targetBatchSize; + + if (state.batchMode === 'random') { + // Generate random batch size within the specified range + const min = Math.max(1, state.randomBatchMin); + const max = Math.max(min, state.randomBatchMax); + targetBatchSize = Math.floor(Math.random() * (max - min + 1)) + min; + console.log(`🎲 Random batch size generated: ${targetBatchSize} (range: ${min}-${max})`); + } else { + // Normal mode - use the fixed paintingSpeed value + targetBatchSize = state.paintingSpeed; + } + + // Always limit by available charges + const maxAllowed = Math.floor(state.currentCharges); + const finalBatchSize = Math.min(targetBatchSize, maxAllowed); + + return finalBatchSize; + } + + // Helper function to retry batch until success with exponential backoff + async function sendBatchWithRetry(pixels, regionX, regionY, maxRetries = MAX_BATCH_RETRIES) { + let attempt = 0; + while (attempt < maxRetries && !state.stopFlag) { + attempt++; + console.log(`🔄 Attempting to send batch (attempt ${attempt}/${maxRetries}) for region ${regionX},${regionY} with ${pixels.length} pixels`); + + const result = await sendPixelBatch(pixels, regionX, regionY); + + if (result === true) { + console.log(`✅ Batch succeeded on attempt ${attempt}`); + return true; + } else if (result === "token_error") { + console.log(`🔑 Token error on attempt ${attempt}, regenerating...`); + updateUI("captchaSolving", "warning"); + try { + await handleCaptcha(); + // Don't count token regeneration as a failed attempt + attempt--; + continue; + } catch (e) { + console.error(`❌ Token regeneration failed on attempt ${attempt}:`, e); + updateUI("captchaFailed", "error"); + // Wait longer before retrying after token failure + await Utils.sleep(5000); + } + } else { + console.warn(`⚠️ Batch failed on attempt ${attempt}, retrying...`); + // Exponential backoff with jitter + const baseDelay = Math.min(1000 * Math.pow(2, attempt - 1), 30000); // Max 30s + const jitter = Math.random() * 1000; // Add up to 1s random delay + await Utils.sleep(baseDelay + jitter); + } + } + + if (attempt >= maxRetries) { + console.error(`❌ Batch failed after ${maxRetries} attempts (MAX_BATCH_RETRIES=${MAX_BATCH_RETRIES}). This will stop painting to prevent infinite loops.`); + updateUI("paintingError", "error"); + return false; + } + + return false; + } + + async function sendPixelBatch(pixelBatch, regionX, regionY) { + let token = turnstileToken; + + // Generate new token if we don't have one + if (!token) { + try { + console.log("🔑 Generating Turnstile token for pixel batch..."); + token = await handleCaptcha(); + turnstileToken = token; // Store for potential reuse + } catch (error) { + console.error("❌ Failed to generate Turnstile token:", error); + tokenPromise = new Promise((resolve) => { _resolveToken = resolve }); + return "token_error"; + } + } + + const coords = new Array(pixelBatch.length * 2) + const colors = new Array(pixelBatch.length) + for (let i = 0; i < pixelBatch.length; i++) { + const pixel = pixelBatch[i] + coords[i * 2] = pixel.x + coords[i * 2 + 1] = pixel.y + colors[i] = pixel.color + } + + try { + const payload = { coords, colors, t: token, fp: randStr(10) }; + var wasmtoken = await createWasmToken(regionX, regionY, payload); + const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { + method: 'POST', + headers: { 'Content-Type': 'text/plain;charset=UTF-8', "x-pawtect-token":wasmtoken }, + credentials: 'include', + body: JSON.stringify(payload), + }); + + if (res.status === 403) { + let data = null + try { data = await res.json() } catch (_) { } + console.error("❌ 403 Forbidden. Turnstile token might be invalid or expired.") + + // Try to generate a new token and retry once + try { + console.log("🔄 Regenerating Turnstile token after 403..."); + token = await handleCaptcha(); + turnstileToken = token; + + // Retry the request with new token + const retryPayload = { coords, colors, t: token, fp: randStr(10) }; + var wasmtoken = await createWasmToken(regionX, regionY, retryPayload); + const retryRes = await fetch( + `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, + { + method: 'POST', + headers: { 'Content-Type': 'text/plain;charset=UTF-8', "x-pawtect-token":wasmtoken }, + credentials: 'include', + body: JSON.stringify(retryPayload), + } + ); + + if (retryRes.status === 403) { + turnstileToken = null; + tokenPromise = new Promise((resolve) => { _resolveToken = resolve }); + return "token_error"; + } + + const retryData = await retryRes.json(); + return retryData?.painted === pixelBatch.length; + + } catch (retryError) { + console.error("❌ Token regeneration failed:", retryError); + turnstileToken = null; + tokenPromise = new Promise((resolve) => { _resolveToken = resolve }); + return "token_error"; + } + } + + const data = await res.json() + return data?.painted === pixelBatch.length + } catch (e) { + console.error("Batch paint request failed:", e) + return false + } + } + + function saveBotSettings() { + try { + const settings = { + paintingSpeed: state.paintingSpeed, + paintingSpeedEnabled: document.getElementById('enableSpeedToggle')?.checked, + batchMode: state.batchMode, // "normal" or "random" + randomBatchMin: state.randomBatchMin, + randomBatchMax: state.randomBatchMax, + cooldownChargeThreshold: state.cooldownChargeThreshold, + autoSwap: CONFIG.autoSwap, + tokenSource: state.tokenSource, // "generator", "hybrid", or "manual" + minimized: state.minimized, + overlayOpacity: state.overlayOpacity, + blueMarbleEnabled: document.getElementById('enableBlueMarbleToggle')?.checked, + ditheringEnabled: state.ditheringEnabled, + colorMatchingAlgorithm: state.colorMatchingAlgorithm, + enableChromaPenalty: state.enableChromaPenalty, + chromaPenaltyWeight: state.chromaPenaltyWeight, + customTransparencyThreshold: state.customTransparencyThreshold, + customWhiteThreshold: state.customWhiteThreshold, + paintWhitePixels: state.paintWhitePixels, + resizeSettings: state.resizeSettings, + originalImage: state.originalImage, + // Save ignore mask (as base64) with its dimensions + resizeIgnoreMask: (state.resizeIgnoreMask && state.resizeSettings && state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length) + ? { w: state.resizeSettings.width, h: state.resizeSettings.height, data: btoa(String.fromCharCode(...state.resizeIgnoreMask)) } + : null, + // Notifications + notificationsEnabled: state.notificationsEnabled, + notifyOnChargesReached: state.notifyOnChargesReached, + notifyOnlyWhenUnfocused: state.notifyOnlyWhenUnfocused, + notificationIntervalMinutes: state.notificationIntervalMinutes, + }; + CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled; + // AUTO_CAPTCHA_ENABLED is always true - no need to save/load + + localStorage.setItem("wplace-bot-settings", JSON.stringify(settings)); + } catch (e) { + console.warn("Could not save bot settings:", e); + } + } + + function loadBotSettings() { + try { + const saved = localStorage.getItem("wplace-bot-settings"); + if (!saved) return; + const settings = JSON.parse(saved); + + state.paintingSpeed = settings.paintingSpeed || CONFIG.PAINTING_SPEED.DEFAULT; + state.batchMode = settings.batchMode || CONFIG.BATCH_MODE; // Default to "normal" + state.randomBatchMin = settings.randomBatchMin || CONFIG.RANDOM_BATCH_RANGE.MIN; + state.randomBatchMax = settings.randomBatchMax || CONFIG.RANDOM_BATCH_RANGE.MAX; + state.cooldownChargeThreshold = settings.cooldownChargeThreshold || CONFIG.COOLDOWN_CHARGE_THRESHOLD; + CONFIG.autoSwap = settings.autoSwap ?? false; + state.tokenSource = settings.tokenSource || CONFIG.TOKEN_SOURCE; // Default to "generator" + state.minimized = settings.minimized ?? false; + CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled ?? false; + CONFIG.AUTO_CAPTCHA_ENABLED = settings.autoCaptchaEnabled ?? false; + state.overlayOpacity = settings.overlayOpacity ?? CONFIG.OVERLAY.OPACITY_DEFAULT; + state.blueMarbleEnabled = settings.blueMarbleEnabled ?? CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT; + state.ditheringEnabled = settings.ditheringEnabled ?? false; + state.colorMatchingAlgorithm = settings.colorMatchingAlgorithm || 'lab'; + state.enableChromaPenalty = settings.enableChromaPenalty ?? true; + state.chromaPenaltyWeight = settings.chromaPenaltyWeight ?? 0.15; + state.customTransparencyThreshold = settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; + state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; + state.paintWhitePixels = settings.paintWhitePixels ?? true; + state.resizeSettings = settings.resizeSettings ?? null; + state.originalImage = settings.originalImage ?? null; + // Notifications + state.notificationsEnabled = settings.notificationsEnabled ?? CONFIG.NOTIFICATIONS.ENABLED; + state.notifyOnChargesReached = settings.notifyOnChargesReached ?? CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED; + state.notifyOnlyWhenUnfocused = settings.notifyOnlyWhenUnfocused ?? CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED; + state.notificationIntervalMinutes = settings.notificationIntervalMinutes ?? CONFIG.NOTIFICATIONS.REPEAT_MINUTES; + // Restore ignore mask if dims match current resizeSettings + if (settings.resizeIgnoreMask && settings.resizeIgnoreMask.data && state.resizeSettings && settings.resizeIgnoreMask.w === state.resizeSettings.width && settings.resizeIgnoreMask.h === state.resizeSettings.height) { + try { + const bin = atob(settings.resizeIgnoreMask.data); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i); + state.resizeIgnoreMask = arr; + } catch { state.resizeIgnoreMask = null; } + } else { + state.resizeIgnoreMask = null; + } + + const speedSlider = document.getElementById('speedSlider'); + if (speedSlider) speedSlider.value = state.paintingSpeed; + const speedValue = document.getElementById('speedValue'); + if (speedValue) speedValue.textContent = `${state.paintingSpeed} (batch size)`; + + const enableSpeedToggle = document.getElementById('enableSpeedToggle'); + if (enableSpeedToggle) enableSpeedToggle.checked = CONFIG.PAINTING_SPEED_ENABLED; + + // Batch mode UI initialization + const batchModeSelect = document.getElementById('batchModeSelect'); + if (batchModeSelect) batchModeSelect.value = state.batchMode; + + const normalBatchControls = document.getElementById('normalBatchControls'); + const randomBatchControls = document.getElementById('randomBatchControls'); + + // Show/hide appropriate controls based on batch mode + if (normalBatchControls && randomBatchControls) { + if (state.batchMode === 'random') { + normalBatchControls.style.display = 'none'; + randomBatchControls.style.display = 'block'; + } else { + normalBatchControls.style.display = 'block'; + randomBatchControls.style.display = 'none'; + } + } + + const randomBatchMin = document.getElementById('randomBatchMin'); + if (randomBatchMin) randomBatchMin.value = state.randomBatchMin; + + const randomBatchMax = document.getElementById('randomBatchMax'); + if (randomBatchMax) randomBatchMax.value = state.randomBatchMax; + + // AUTO_CAPTCHA_ENABLED is always true - no toggle to set + + const cooldownSlider = document.getElementById('cooldownSlider'); + if (cooldownSlider) cooldownSlider.value = state.cooldownChargeThreshold; + const cooldownValue = document.getElementById('cooldownValue'); + if (cooldownValue) cooldownValue.textContent = state.cooldownChargeThreshold; + + const autoSwapToggle = document.getElementById('autoSwapToggle'); + if (autoSwapToggle) autoSwapToggle.checked = CONFIG.autoSwap; + + const overlayOpacitySlider = document.getElementById('overlayOpacitySlider'); + if (overlayOpacitySlider) overlayOpacitySlider.value = state.overlayOpacity; + const overlayOpacityValue = document.getElementById('overlayOpacityValue'); + if (overlayOpacityValue) overlayOpacityValue.textContent = `${Math.round(state.overlayOpacity * 100)}%`; + const enableBlueMarbleToggle = document.getElementById('enableBlueMarbleToggle'); + if (enableBlueMarbleToggle) enableBlueMarbleToggle.checked = state.blueMarbleEnabled; + + const tokenSourceSelect = document.getElementById('tokenSourceSelect'); + if (tokenSourceSelect) tokenSourceSelect.value = state.tokenSource; + + const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); + if (colorAlgorithmSelect) colorAlgorithmSelect.value = state.colorMatchingAlgorithm; + const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); + if (enableChromaPenaltyToggle) enableChromaPenaltyToggle.checked = state.enableChromaPenalty; + const chromaPenaltyWeightSlider = document.getElementById('chromaPenaltyWeightSlider'); + if (chromaPenaltyWeightSlider) chromaPenaltyWeightSlider.value = state.chromaPenaltyWeight; + const chromaWeightValue = document.getElementById('chromaWeightValue'); + if (chromaWeightValue) chromaWeightValue.textContent = state.chromaPenaltyWeight; + const transparencyThresholdInput = document.getElementById('transparencyThresholdInput'); + if (transparencyThresholdInput) transparencyThresholdInput.value = state.customTransparencyThreshold; + const whiteThresholdInput = document.getElementById('whiteThresholdInput'); + if (whiteThresholdInput) whiteThresholdInput.value = state.customWhiteThreshold; + // Notifications UI + const notifEnabledToggle = document.getElementById('notifEnabledToggle'); + if (notifEnabledToggle) notifEnabledToggle.checked = state.notificationsEnabled; + const notifOnChargesToggle = document.getElementById('notifOnChargesToggle'); + if (notifOnChargesToggle) notifOnChargesToggle.checked = state.notifyOnChargesReached; + const notifOnlyUnfocusedToggle = document.getElementById('notifOnlyUnfocusedToggle'); + if (notifOnlyUnfocusedToggle) notifOnlyUnfocusedToggle.checked = state.notifyOnlyWhenUnfocused; + const notifIntervalInput = document.getElementById('notifIntervalInput'); + if (notifIntervalInput) notifIntervalInput.value = state.notificationIntervalMinutes; + NotificationManager.resetEdgeTracking(); + + } catch (e) { + console.warn("Could not load bot settings:", e); + } + } + + // Initialize Turnstile generator integration + console.log("🚀 WPlace Auto-Image with Turnstile Token Generator loaded"); + console.log("🔑 Turnstile token generator: ALWAYS ENABLED (Background mode)"); + console.log("🎯 Manual pixel captcha solving: Available as fallback/alternative"); + console.log("📱 Turnstile widgets: DISABLED - pure background token generation only!"); + + // Function to enable file operations after initial startup setup is complete + function enableFileOperations() { + state.initialSetupComplete = true; + + const loadBtn = document.querySelector("#loadBtn"); + const loadFromFileBtn = document.querySelector("#loadFromFileBtn"); + const uploadBtn = document.querySelector("#uploadBtn"); + + if (loadBtn) { + loadBtn.disabled = false; + loadBtn.title = ""; + // Add a subtle animation to indicate the button is now available + loadBtn.style.animation = "pulse 0.6s ease-in-out"; + setTimeout(() => { + if (loadBtn) loadBtn.style.animation = ""; + }, 600); + console.log("✅ Load Progress button enabled after initial setup"); + } + + if (loadFromFileBtn) { + loadFromFileBtn.disabled = false; + loadFromFileBtn.title = ""; + // Add a subtle animation to indicate the button is now available + loadFromFileBtn.style.animation = "pulse 0.6s ease-in-out"; + setTimeout(() => { + if (loadFromFileBtn) loadFromFileBtn.style.animation = ""; + }, 600); + console.log("✅ Load from File button enabled after initial setup"); + } + + if (uploadBtn) { + uploadBtn.disabled = false; + uploadBtn.title = ""; + // Add a subtle animation to indicate the button is now available + uploadBtn.style.animation = "pulse 0.6s ease-in-out"; + setTimeout(() => { + if (uploadBtn) uploadBtn.style.animation = ""; + }, 600); + console.log("✅ Upload Image button enabled after initial setup"); + } + + // Show a notification that file operations are now available + Utils.showAlert("📂 File operations (Load/Upload) are now available!", "success"); + } + + // Optimized token initialization with better timing and error handling + async function initializeTokenGenerator() { + // Skip if already have valid token + if (isTokenValid()) { + console.log("✅ Valid token already available, skipping initialization"); + updateUI("tokenReady", "success"); + enableFileOperations(); // Enable file operations since initial setup is complete + return; + } + + try { + console.log("🔧 Initializing Turnstile token generator..."); + updateUI("initializingToken", "default"); + + // Pre-load Turnstile script first to avoid delays later + await Utils.loadTurnstile(); + + const token = await handleCaptchaWithRetry(); + if (token) { + setTurnstileToken(token); + console.log("✅ Startup token generated successfully"); + updateUI("tokenReady", "success"); + Utils.showAlert("🔑 Token generator ready!", "success"); + enableFileOperations(); // Enable file operations since initial setup is complete + } else { + console.warn("⚠️ Startup token generation failed, will retry when needed"); + updateUI("tokenRetryLater", "warning"); + // Still enable file operations even if initial token generation fails + // Users can load progress and use manual/hybrid modes + enableFileOperations(); + } + } catch (error) { + console.warn("⚠️ Startup token generation failed:", error); + updateUI("tokenRetryLater", "warning"); + // Still enable file operations even if initial setup fails + // Users can load progress and use manual/hybrid modes + enableFileOperations(); + // Don't show error alert for initialization failures, just log them + } + } + + // Load theme preference immediately on startup before creating UI + loadThemePreference(); + async function createWasmToken(regionX,regionY, payload) { + try { + // Load the Pawtect module and WASM + + async function getModURL(update) { + if (update === true || localStorage.getItem('pawtect-wasm-location') === null) { + const rootHTML = (await (await fetch(window.location.href)).text()).split('\n') + + for (const line of rootHTML) { + if (line.includes(' 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); + } + + // Create test payload + + 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); + } + + // Display results + console.log(''); + console.log('🎉 SUCCESS!'); + console.log('📊 Results:'); + console.log(' Input coords: [1245984, 1088]'); + console.log(' Token length:', token?.length || 0); + console.log(' Token preview:', token?.substring(0, 50) + '...'); + console.log(''); + console.log('🔑 Full token:'); + console.log(token); + return token; + } catch (error) { + console.error('❌ Failed to generate fp parameter:', error); + return null; + } + return null; + } + + async function purchase(type) { + // loadThemePreference() + let id; + let chargeMultiplier; + if (type === "max_charges") { + id = 70; + chargeMultiplier = 5; + } else if (type === "paint_charges") { + id = 80; + chargeMultiplier = 30; + } else { + console.error("Error: Invalid purchase type provided."); + return; + } + const { droplets } = await WPlaceService.getCharges(); + console.log("There are currently : ", droplets, "droplets."); + try { + const amounts = Math.floor(droplets / 500); + if (amounts < 1) { + console.log("Not enough droplets to purchase."); + return; + } + const payload = { + "product": { + "id": id, + "amount": amounts + } + }; + const res = fetch("https://backend.wplace.live/purchase", { + method: "POST", + headers: { + "Content-Type": "text/plain;charset=UTF-8" + }, + body: JSON.stringify(payload), + credentials: "include" + }); + console.log("Fetch POST return :", res); + const { droplets: newDroplets } = await WPlaceService.getCharges(); + if (droplets != newDroplets) { + console.log("Successfully bought", amounts * chargeMultiplier, type.replace('_', ' '), "."); + } + else { + console.log("Failed to buy charges"); + } + } catch (e) { + console.error("An error occurred during the purchase:", e); + } + } + function swapAccountTrigger(token) { + localStorage.removeItem("lp"); + if (!token) return; + console.log("Sending token to extension..."); + window.postMessage({ + source: 'my-userscript', + type: 'setCookie', + value: token + }, '*'); + } + async function getAccounts() { + return new Promise((resolve, reject) => { + console.log("Requesting accounts from extension..."); + // Ask extension for accounts + window.postMessage({ + source: "my-userscript", + type: "getAccounts" + }, "*"); + function handler(event) { + if (event.source !== window) return; + if (event.data.source !== "extension") return; + if (event.data.type === "accountsData") { + window.removeEventListener("message", handler); + // Save to localStorage + try { + localStorage.setItem("accounts", JSON.stringify(event.data.accounts)); + console.log("✅ Accounts saved to localStorage:", event.data.accounts); + } catch (e) { + console.error("❌ Failed to save accounts:", e); + } + resolve(event.data.accounts); + } + } + window.addEventListener("message", handler); + }); + } + async function fetchAccount() { + const { ID, Charges, Max, Droplets } = await WPlaceService.fetchCheck(); + console.log("User's ID :", ID); + console.log("User's Charges :", Charges, "/", Max); + console.log("User's Droplets :", Droplets); + } + createUI().then(() => { + // Generate token automatically after UI is ready + setTimeout(initializeTokenGenerator, 1000); + + // Attach advanced color matching listeners (resize dialog) + const advancedInit = () => { + const chromaSlider = document.getElementById('chromaPenaltyWeightSlider'); + const chromaValue = document.getElementById('chromaWeightValue'); + const resetBtn = document.getElementById('resetAdvancedColorBtn'); + const algoSelect = document.getElementById('colorAlgorithmSelect'); + const chromaToggle = document.getElementById('enableChromaPenaltyToggle'); + const transInput = document.getElementById('transparencyThresholdInput'); + const whiteInput = document.getElementById('whiteThresholdInput'); + const ditherToggle = document.getElementById('enableDitheringToggle'); + if (algoSelect) algoSelect.addEventListener('change', e => { state.colorMatchingAlgorithm = e.target.value; saveBotSettings(); _updateResizePreview(); }); + if (chromaToggle) chromaToggle.addEventListener('change', e => { state.enableChromaPenalty = e.target.checked; saveBotSettings(); _updateResizePreview(); }); + if (chromaSlider && chromaValue) chromaSlider.addEventListener('input', e => { state.chromaPenaltyWeight = parseFloat(e.target.value) || 0.15; chromaValue.textContent = state.chromaPenaltyWeight.toFixed(2); saveBotSettings(); _updateResizePreview(); }); + if (transInput) transInput.addEventListener('change', e => { const v = parseInt(e.target.value, 10); if (!isNaN(v) && v >= 0 && v <= 255) { state.customTransparencyThreshold = v; CONFIG.TRANSPARENCY_THRESHOLD = v; saveBotSettings(); _updateResizePreview(); } }); + if (whiteInput) whiteInput.addEventListener('change', e => { const v = parseInt(e.target.value, 10); if (!isNaN(v) && v >= 200 && v <= 255) { state.customWhiteThreshold = v; CONFIG.WHITE_THRESHOLD = v; saveBotSettings(); _updateResizePreview(); } }); + if (ditherToggle) ditherToggle.addEventListener('change', e => { state.ditheringEnabled = e.target.checked; saveBotSettings(); _updateResizePreview(); }); + if (resetBtn) resetBtn.addEventListener('click', () => { + state.colorMatchingAlgorithm = 'lab'; state.enableChromaPenalty = true; state.chromaPenaltyWeight = 0.15; state.customTransparencyThreshold = CONFIG.TRANSPARENCY_THRESHOLD = 100; state.customWhiteThreshold = CONFIG.WHITE_THRESHOLD = 250; saveBotSettings(); const a = document.getElementById('colorAlgorithmSelect'); if (a) a.value = 'lab'; const ct = document.getElementById('enableChromaPenaltyToggle'); if (ct) ct.checked = true; if (chromaSlider) chromaSlider.value = 0.15; if (chromaValue) chromaValue.textContent = '0.15'; if (transInput) transInput.value = 100; if (whiteInput) whiteInput.value = 250; _updateResizePreview(); Utils.showAlert('Advanced color settings reset.', 'success'); + }); + }; + // Delay to ensure resize UI built + setTimeout(advancedInit, 500); + + // Add cleanup on page unload + window.addEventListener('beforeunload', () => { + Utils.cleanupTurnstile(); + }); + }) +})() \ No newline at end of file diff --git a/Extension/scripts/Auto-Image.js b/Extension/scripts/Auto-Image.js index a379d53..7cef737 100644 --- a/Extension/scripts/Auto-Image.js +++ b/Extension/scripts/Auto-Image.js @@ -38,7 +38,7 @@ localStorage.removeItem("lp"); REPEAT_MINUTES: 5, // repeat reminder while threshold condition holds }, OVERLAY: { - OPACITY_DEFAULT: 0.2, + OPACITY_DEFAULT: 0.6, BLUE_MARBLE_DEFAULT: false, ditheringEnabled: false, }, // --- START: Color data from colour-converter.js --- @@ -229,6 +229,7 @@ localStorage.removeItem("lp"); COORDINATE_SNAKE: true, COORDINATE_BLOCK_WIDTH: 6, COORDINATE_BLOCK_HEIGHT: 2, + autoSwap: true, }; const getCurrentTheme = () => CONFIG.THEMES[CONFIG.currentTheme]; @@ -251,7 +252,7 @@ localStorage.removeItem("lp"); const theme = getCurrentTheme(); console.group('%c🎨 Applying Theme in Auto-Image Script', 'color: #8b5cf6; font-weight: bold;'); console.log(`%c🎯 Target theme: ${CONFIG.currentTheme}`, 'color: #8b5cf6;'); - + // Toggle theme class on documentElement so CSS vars cascade to our UI document.documentElement.classList.remove( 'wplace-theme-classic', @@ -262,7 +263,7 @@ localStorage.removeItem("lp"); let themeClass = 'wplace-theme-classic'; // default let themeFileName = 'classic'; // corresponding file name - + if (CONFIG.currentTheme === 'Neon Retro') { themeClass = 'wplace-theme-neon'; themeFileName = 'neon'; @@ -320,7 +321,7 @@ localStorage.removeItem("lp"); setVar('--wplace-border-width', '' + (theme.borderWidth || '1px')); setVar('--wplace-backdrop', '' + (theme.backdropFilter || 'blur(10px)')); setVar('--wplace-border-color', 'rgba(255,255,255,0.1)'); - + console.log(`%c🎨 Theme application complete`, 'color: #8b5cf6; font-weight: bold;'); console.groupEnd(); } @@ -377,11 +378,11 @@ localStorage.removeItem("lp"); // First try: Check if extension has loaded local resources if (window.AUTOBOT_LANGUAGES && Object.keys(window.AUTOBOT_LANGUAGES).length > 0) { console.log(`%c🔍 Checking extension local resources...`, 'color: #06b6d4;'); - + const langFile = language + '.json'; if (window.AUTOBOT_LANGUAGES[langFile]) { const translations = window.AUTOBOT_LANGUAGES[langFile]; - + // Validate that translations is an object with keys if ( typeof translations === 'object' && @@ -721,6 +722,7 @@ localStorage.removeItem("lp"); _lastSaveTime: 0, _saveInProgress: false, paintedMap: null, + accountIndex: -1, }; let _updateResizePreview = () => { }; @@ -2976,34 +2978,49 @@ localStorage.removeItem("lp"); }, async getCharges() { - const defaultResult = { - charges: 0, - max: 1, - cooldown: CONFIG.COOLDOWN_DEFAULT, - }; - try { - const res = await fetch('https://backend.wplace.live/me', { - credentials: 'include', - }); - - if (!res.ok) { - console.error(`Failed to get charges: HTTP ${res.status}`); - return defaultResult; - } - - const data = await res.json(); - + const res = await fetch("https://backend.wplace.live/me", { + credentials: "include", + }) + const data = await res.json() return { - charges: data.charges?.count ?? 0, - max: data.charges?.max ?? 1, - cooldown: data.charges?.cooldownMs ?? CONFIG.COOLDOWN_DEFAULT, - }; + id: data.id, + charges: data.charges?.count || 0, + max: data.charges?.max || 1, + cooldown: data.charges?.next || CONFIG.COOLDOWN_DEFAULT, + droplets: data.droplets || 0, + } } catch (e) { - console.error('Failed to get charges:', e); - return defaultResult; + console.error("Failed to get charges:", e) + return { + id: null, + charges: 0, + max: 1, + cooldown: CONFIG.COOLDOWN_DEFAULT, + droplets: 0, + } } }, + + async fetchCheck() { + try { + const res = await fetch("https://backend.wplace.live/me", { + credentials: "include", + }) + const data = await res.json() + return { + ID: data.id, + Charges: data.charges.count, + Max: data.charges.max, + Droplets: data.droplets + } + } catch (e) { + console.error("Failed to get ID:", e) + return { + + } + } + } }; // Desktop Notification Manager @@ -3515,13 +3532,13 @@ localStorage.removeItem("lp"); // Load auto-image-styles.css - prioritize extension local resources console.group('%c🎨 Loading Auto-Image Styles', 'color: #8b5cf6; font-weight: bold;'); - + let stylesLoaded = false; - + // First try: Check if extension has loaded local CSS resources if (window.AUTOBOT_THEMES && window.AUTOBOT_THEMES['auto-image-styles.css']) { console.log('%c🔍 Found auto-image-styles.css in extension local resources!', 'color: #10b981; font-weight: bold;'); - + // Check if it's already injected by the extension const existingStyle = document.getElementById('autobot-auto-image-styles'); if (existingStyle) { @@ -3536,7 +3553,7 @@ localStorage.removeItem("lp"); styleElement.setAttribute('data-wplace-theme', 'true'); styleElement.textContent = window.AUTOBOT_THEMES['auto-image-styles.css']; document.head.appendChild(styleElement); - + console.log('%c✅ Injected auto-image-styles.css from extension local resources', 'color: #10b981; font-weight: bold;'); console.log(' 📍 Source: Extension local file'); console.log(' 📏 Size: ' + window.AUTOBOT_THEMES['auto-image-styles.css'].length + ' characters'); @@ -3550,7 +3567,7 @@ localStorage.removeItem("lp"); console.log(' 📋 Available themes:', Object.keys(window.AUTOBOT_THEMES)); } } - + // Fallback: Load from CDN if local resources not available if (!stylesLoaded) { console.log('%c🌐 Falling back to CDN loading...', 'color: #8b5cf6;'); @@ -3563,13 +3580,13 @@ localStorage.removeItem("lp"); console.log(' 🌐 URL: https://wplace-autobot.github.io/WPlace-AutoBOT/main/auto-image-styles.css'); console.log(' ⚠️ Performance: Network request required'); } - + console.groupEnd(); // Ensure default theme is loaded from extension resources if (window.applyTheme && typeof window.applyTheme === 'function') { console.group('%c🎨 Loading Default Theme from Extension', 'color: #8b5cf6; font-weight: bold;'); - + // Determine the current theme file name let defaultTheme = 'classic'; // fallback if (CONFIG.currentTheme === 'Neon Retro') { @@ -3579,10 +3596,10 @@ localStorage.removeItem("lp"); } else if (CONFIG.currentTheme === 'Acrylic') { defaultTheme = 'acrylic'; } - + console.log(`%c🎯 Loading theme: ${defaultTheme} (${CONFIG.currentTheme})`, 'color: #8b5cf6;'); const success = window.applyTheme(defaultTheme); - + if (success) { console.log(`%c✅ Default theme loaded from extension local resources`, 'color: #10b981; font-weight: bold;'); console.log(` 📍 Source: Extension local file (themes/${defaultTheme}.css)`); @@ -3592,7 +3609,7 @@ localStorage.removeItem("lp"); console.warn(`%c⚠️ Failed to load theme ${defaultTheme} from extension`, 'color: #f59e0b;'); console.log(` 📝 Note: Theme CSS classes will still work`); } - + console.groupEnd(); } else { console.warn(`%c⚠️ Extension applyTheme() function not available`, 'color: #f59e0b; font-weight: bold;'); @@ -6834,6 +6851,7 @@ localStorage.removeItem("lp"); updateUI('startPaintingMsg', 'success'); try { + await getAccounts(); await processImage(); } catch (e) { console.error('Unexpected error:', e); @@ -7422,20 +7440,79 @@ localStorage.removeItem("lp"); pixelBatch.pixels = []; } + if (!CONFIG.autoSwap) { + if (state.displayCharges < state.cooldownChargeThreshold && !state.stopFlag) { + await Utils.dynamicSleep(() => { + if (state.displayCharges >= state.cooldownChargeThreshold) { + NotificationManager.maybeNotifyChargesReached(true); + return 0; + } + if (state.stopFlag) return 0; + return getMsToTargetCharges( + state.preciseCurrentCharges, + state.cooldownChargeThreshold, + state.cooldown + ); + }); + } + } + else { + if (state.displayCharges < state.cooldownChargeThreshold && !state.stopFlag) { + console.log("⚠️ Charges too low, swapping to next account..."); - if (state.displayCharges < state.cooldownChargeThreshold && !state.stopFlag) { - await Utils.dynamicSleep(() => { - if (state.displayCharges >= state.cooldownChargeThreshold) { - NotificationManager.maybeNotifyChargesReached(true); - return 0; + const accounts = JSON.parse(localStorage.getItem("accounts")) || []; + if (accounts.length === 0) { + console.warn("❌ No accounts available, stopping painting."); + state.stopFlag = true; + return; } - if (state.stopFlag) return 0; - return getMsToTargetCharges( - state.preciseCurrentCharges, - state.cooldownChargeThreshold, - state.cooldown - ); - }); + + state.accountIndex = (state.accountIndex + 1) % accounts.length; + console.log("🔄 Switching to account index:", state.accountIndex); + + const nextToken = accounts[state.accountIndex]; + console.log("🔑 Next token:", nextToken); + + if (!nextToken) { + console.warn("⚠️ Invalid token, skipping..."); + return; + } + + swapAccountTrigger(nextToken); + + let maxRetries = 20; + let retryCount = 0; + let swapSuccess = false; + + while (!swapSuccess && retryCount < maxRetries) { + console.log(`⏳ Waiting for account swap... (Attempt ${retryCount + 1}/${maxRetries})`); + + // Wait for a short period before checking. + await new Promise(resolve => setTimeout(resolve, 1000)); + + try { + await fetchAccount(); + + console.log("✅ Account swap confirmed."); + swapSuccess = true; + } catch (error) { + console.warn("❌ Account swap not yet successful. Retrying...", error); + retryCount++; + } + } + + if (swapSuccess) { + + const { charges, cooldown } = await WPlaceService.getCharges(); + state.displayCharges = Math.floor(charges); + state.cooldown = cooldown; + Utils.performSmartSave(); + updateStats(); + } else { + console.error("❌ Failed to swap account after multiple retries. Stopping loop."); + state.stopFlag = true; + } + } } if (state.stopFlag) { @@ -7998,7 +8075,7 @@ localStorage.removeItem("lp"); 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); + const mod = await import(new URL('/_app/immutable/chunks/' + pawtect_chunk, location.origin).href); let wasm; try { wasm = await mod._(); @@ -8130,6 +8207,97 @@ localStorage.removeItem("lp"); console.error(`❌ Could not find Pawtect chunk: `, error); } + async function purchase(type) { + // loadThemePreference() + let id; + let chargeMultiplier; + if (type === "max_charges") { + id = 70; + chargeMultiplier = 5; + } else if (type === "paint_charges") { + id = 80; + chargeMultiplier = 30; + } else { + console.error("Error: Invalid purchase type provided."); + return; + } + const { droplets } = await WPlaceService.getCharges(); + console.log("There are currently : ", droplets, "droplets."); + try { + const amounts = Math.floor(droplets / 500); + if (amounts < 1) { + console.log("Not enough droplets to purchase."); + return; + } + const payload = { + "product": { + "id": id, + "amount": amounts + } + }; + const res = fetch("https://backend.wplace.live/purchase", { + method: "POST", + headers: { + "Content-Type": "text/plain;charset=UTF-8" + }, + body: JSON.stringify(payload), + credentials: "include" + }); + console.log("Fetch POST return :", res); + const { droplets: newDroplets } = await WPlaceService.getCharges(); + if (droplets != newDroplets) { + console.log("Successfully bought", amounts * chargeMultiplier, type.replace('_', ' '), "."); + } + else { + console.log("Failed to buy charges"); + } + } catch (e) { + console.error("An error occurred during the purchase:", e); + } + } + function swapAccountTrigger(token) { + localStorage.removeItem("lp"); + if (!token) return; + console.log("Sending token to extension..."); + window.postMessage({ + source: 'my-userscript', + type: 'setCookie', + value: token + }, '*'); + } + async function getAccounts() { + return new Promise((resolve, reject) => { + console.log("Requesting accounts from extension..."); + // Ask extension for accounts + window.postMessage({ + source: "my-userscript", + type: "getAccounts" + }, "*"); + function handler(event) { + if (event.source !== window) return; + if (event.data.source !== "extension") return; + if (event.data.type === "accountsData") { + window.removeEventListener("message", handler); + // Save to localStorage + try { + localStorage.setItem("accounts", JSON.stringify(event.data.accounts)); + console.log("✅ Accounts saved to localStorage:", event.data.accounts); + } catch (e) { + console.error("❌ Failed to save accounts:", e); + } + resolve(event.data.accounts); + } + } + window.addEventListener("message", handler); + }); + } + async function fetchAccount() { + const { ID, Charges, Max, Droplets } = await WPlaceService.fetchCheck(); + console.log("User's ID :", ID); + console.log("User's Charges :", Charges, "/", Max); + console.log("User's Droplets :", Droplets); + } + createUI().then(() => { // Generate token automatically after UI is ready setTimeout(initializeTokenGenerator, 1000); diff --git a/Extension/scripts/Auto-Repair.js b/Extension/scripts/Auto-Repair.js index bbcbd92..7cd9677 100644 --- a/Extension/scripts/Auto-Repair.js +++ b/Extension/scripts/Auto-Repair.js @@ -17,6 +17,11 @@ TOKEN_PRELOAD_BUFFER: 60000, // Preload tokens 1 minute before expiry MAX_RETRIES: 10, RETRY_DELAY_BASE: 1000, + // Auto-batch configuration + AUTO_BATCH_ENABLED: true, // Enable auto pixel batch sizing + MAX_BATCH_SIZE: 500, // Maximum pixels per batch + MIN_BATCH_SIZE: 1, // Minimum pixels per batch + BATCH_OPTIMIZATION: true, // Optimize batch size based on detected damage 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 } }, @@ -100,7 +105,7 @@ startPosition: null, region: null, paintWhitePixels: true, - paintTransparentPixels: false, + paintTransparentPixels: true, // Changed to true to fix transparent pixel detection autoRepairEnabled: false, autoRepairInterval: 30, autoRepairTimer: null, @@ -117,6 +122,10 @@ windowMinimized: false, lastAttackState: null, initialSetupComplete: false, + // Auto-batch state + autoBatchEnabled: CONFIG.AUTO_BATCH_ENABLED, + currentBatchSize: CONFIG.MIN_BATCH_SIZE, + batchOptimization: CONFIG.BATCH_OPTIMIZATION, }; // Random string generator @@ -125,7 +134,7 @@ const fpStr32 = randStr(32); - // Enhanced Turnstile token handling - Actualizado con la lógica de new.txt + // Enhanced Turnstile token handling - Actualizado con la lógica de new.txt lol let turnstileToken = null; let tokenExpiryTime = 0; let tokenGenerationInProgress = false; @@ -1352,9 +1361,7 @@ const d = cached.data; const a = d[idx + 3]; - if (!state.paintTransparentPixels && a < alphaThresh) { - return null; - } + // Always return pixel data, let caller decide about transparency return [d[idx], d[idx + 1], d[idx + 2], a]; } @@ -1385,10 +1392,7 @@ const data = ctx.getImageData(x, y, 1, 1).data; const a = data[3]; - if (!state.paintTransparentPixels && a < alphaThresh) { - return null; - } - + // Always return pixel data, let caller decide about transparency return [data[0], data[1], data[2], a]; } catch (e) { Utils.addDebugLog(`Error reading pixel from tile ${tileKey}: ${e.message}`, 'error'); @@ -1457,7 +1461,7 @@ const overlayManager = new OverlayManager(); - // Enhanced WPlace API Service with autonomous capabilities - ACTUALIZADO CON NUEVA LÓGICA + // Enhanced WPlace API Service with auto-batch functionality const WPlaceService = { async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount = 0) { try { @@ -1524,6 +1528,83 @@ } }, + // NEW: Auto-batch pixel painting functionality + async paintPixelBatchInRegion(regionX, regionY, pixelBatch, retryCount = 0) { + try { + await ensureToken(); + if (!turnstileToken) { + Utils.addDebugLog('No valid token available for batch paint request', 'error'); + return 'token_error'; + } + + // Prepare batch coordinates and colors + const coords = []; + const colors = []; + + for (const pixel of pixelBatch) { + coords.push(pixel.pixelX, pixel.pixelY); + colors.push(pixel.color); + } + + const payload = { + coords: coords, + colors: colors, + t: turnstileToken, + fp: fpStr32, + }; + + const wasmToken = await createWasmToken(regionX, regionY, payload); + if (!wasmToken) { + Utils.addDebugLog('Failed to generate WASM token for batch', 'error'); + return false; + } + + const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { + method: 'POST', + headers: { + 'Content-Type': 'text/plain;charset=UTF-8', + 'x-pawtect-token': wasmToken + }, + credentials: 'include', + body: JSON.stringify(payload), + }); + + if (res.status === 403) { + Utils.addDebugLog(`403 Forbidden for batch (${pixelBatch.length} pixels). Token invalid/expired.`, 'error'); + invalidateToken(); + + if (retryCount < 2 && state.autonomousMode) { + Utils.addDebugLog(`Autonomous batch retry ${retryCount + 1}/2 for ${pixelBatch.length} pixels`, 'warning'); + await Utils.sleep(1000); + return await this.paintPixelBatchInRegion(regionX, regionY, pixelBatch, retryCount + 1); + } + + return 'token_error'; + } + + if (!res.ok) { + Utils.addDebugLog(`Batch paint request failed with status ${res.status}`, 'error'); + return false; + } + + const data = await res.json(); + const successCount = data?.painted || 0; + + Utils.addDebugLog(`Batch paint result: ${successCount}/${pixelBatch.length} pixels painted successfully`, + successCount === pixelBatch.length ? 'success' : 'warning'); + + return { + success: successCount > 0, + painted: successCount, + total: pixelBatch.length + }; + + } catch (e) { + Utils.addDebugLog(`Batch paint request error for ${pixelBatch.length} pixels: ${e.message}`, 'error'); + return false; + } + }, + async getCharges() { try { const res = await fetch('https://backend.wplace.live/me', { @@ -1543,14 +1624,14 @@ }, }; - // Anti-grief repair system with autonomous capabilities + // Enhanced anti-grief repair system with improved transparent pixel detection async function scanForDamage() { if (!state.imageData || !state.startPosition || !state.region) { Utils.addDebugLog('No image data or position for scanning', 'warning'); return []; } - Utils.addDebugLog('Starting autonomous damage scan...', 'info'); + Utils.addDebugLog('Starting enhanced damage scan with transparent pixel detection...', 'info'); updateStatus(Utils.t('scanningForDamage')); const damagedPixels = []; @@ -1579,9 +1660,11 @@ return []; } - Utils.addDebugLog(`Scanning ${width}x${height} image for damage (autonomous: ${state.autonomousMode})...`, 'info'); + Utils.addDebugLog(`Scanning ${width}x${height} image for damage (transparent detection: ${state.paintTransparentPixels})...`, 'info'); let scannedPixels = 0; + let transparentPixelsDetected = 0; + let wrongColorPixelsDetected = 0; let lastProgressUpdate = Date.now(); for (let y = 0; y < height; y++) { @@ -1594,10 +1677,10 @@ const originalB = pixels[idx + 2]; const originalA = pixels[idx + 3]; - if (!state.paintTransparentPixels && originalA < CONFIG.TRANSPARENCY_THRESHOLD) { - continue; - } - + // Enhanced transparent pixel detection + const isOriginalTransparent = originalA < state.customTransparencyThreshold; + + // Skip white pixels if configured if (!state.paintWhitePixels && Utils.isWhitePixel(originalR, originalG, originalB)) { continue; } @@ -1614,30 +1697,93 @@ try { const currentPixel = await overlayManager.getTilePixelColor(tileX, tileY, pixelX, pixelY); + // Handle missing current pixel data (could indicate transparent area) if (!currentPixel) { - if (!state.autonomousMode) { - Utils.addDebugLog(`No current pixel data for (${x},${y}) - tile ${tileX},${tileY}`, 'warning'); + if (state.paintTransparentPixels && !isOriginalTransparent) { + // Original pixel is not transparent but current is missing/transparent + const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); + damagedPixels.push({ + x, + y, + originalColor: targetColor, + currentColor: { id: 63, name: 'Transparent', rgb: [0, 0, 0] }, // Transparent color + originalRgb: [originalR, originalG, originalB], + currentRgb: [0, 0, 0], + isDamagedTransparent: true + }); + transparentPixelsDetected++; + + if (!state.autonomousMode || transparentPixelsDetected <= 10) { + Utils.addDebugLog(`Missing/Transparent pixel at (${x},${y}): expected color ${targetColor.id}, found transparent/missing`, 'warning'); + } } continue; } - const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); - const currentColor = Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors); + const currentIsTransparent = currentPixel[3] < state.customTransparencyThreshold; - if (targetColor.id !== currentColor.id) { + // Case 1: Original is not transparent, but current is transparent (damaged) + if (!isOriginalTransparent && currentIsTransparent && state.paintTransparentPixels) { + const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); damagedPixels.push({ x, y, originalColor: targetColor, - currentColor: currentColor, + currentColor: { id: 63, name: 'Transparent', rgb: [0, 0, 0] }, originalRgb: [originalR, originalG, originalB], - currentRgb: currentPixel.slice(0, 3) + currentRgb: currentPixel.slice(0, 3), + isDamagedTransparent: true }); + transparentPixelsDetected++; + + if (!state.autonomousMode || transparentPixelsDetected <= 10) { + Utils.addDebugLog(`Transparent damage at (${x},${y}): expected color ${targetColor.id}, found transparent`, 'warning'); + } + continue; + } - if (!state.autonomousMode || damagedPixels.length <= 10) { - Utils.addDebugLog(`Damage at (${x},${y}): expected color ${targetColor.id}, found color ${currentColor.id}`, 'warning'); + // Case 2: Original is transparent, current is not transparent (wrong placement) + if (isOriginalTransparent && !currentIsTransparent && state.paintTransparentPixels) { + damagedPixels.push({ + x, + y, + originalColor: { id: 63, name: 'Transparent', rgb: null }, + currentColor: Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors), + originalRgb: [originalR, originalG, originalB], + currentRgb: currentPixel.slice(0, 3), + isDamagedTransparent: true + }); + transparentPixelsDetected++; + + if (!state.autonomousMode || transparentPixelsDetected <= 10) { + Utils.addDebugLog(`Wrong placement at (${x},${y}): expected transparent, found color`, 'warning'); + } + continue; + } + + // Case 3: Both are not transparent, check color match (normal damage detection) + if (!isOriginalTransparent && !currentIsTransparent) { + const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); + const currentColor = Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors); + + if (targetColor.id !== currentColor.id) { + damagedPixels.push({ + x, + y, + originalColor: targetColor, + currentColor: currentColor, + originalRgb: [originalR, originalG, originalB], + currentRgb: currentPixel.slice(0, 3), + isDamagedTransparent: false + }); + wrongColorPixelsDetected++; + + if (!state.autonomousMode || wrongColorPixelsDetected <= 10) { + Utils.addDebugLog(`Color damage at (${x},${y}): expected color ${targetColor.id}, found color ${currentColor.id}`, 'warning'); + } } } + } catch (e) { if (!state.autonomousMode) { Utils.addDebugLog(`Error checking pixel (${x},${y}): ${e.message}`, 'error'); @@ -1646,7 +1792,7 @@ } if (state.autonomousMode && Date.now() - lastProgressUpdate > 5000) { - Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%), found ${damagedPixels.length} damaged`, 'info'); + Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%), found ${damagedPixels.length} damaged (${transparentPixelsDetected} transparent, ${wrongColorPixelsDetected} wrong color)`, 'info'); lastProgressUpdate = Date.now(); } else if (!state.autonomousMode && y % 10 === 0) { Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%)`, 'info'); @@ -1654,7 +1800,17 @@ } const logLevel = damagedPixels.length > 0 ? 'warning' : 'success'; - Utils.addDebugLog(`Scan complete. Checked ${scannedPixels} pixels, found ${damagedPixels.length} damaged`, logLevel); + Utils.addDebugLog(`Enhanced scan complete. Checked ${scannedPixels} pixels, found ${damagedPixels.length} damaged (${transparentPixelsDetected} transparent issues, ${wrongColorPixelsDetected} wrong colors)`, logLevel); + + // Smart auto-adjust batch size: if 6 pixels detected, batch size = 6 + if (state.autoBatchEnabled && state.batchOptimization && damagedPixels.length > 0) { + const newBatchSize = Math.min(CONFIG.MAX_BATCH_SIZE, Math.max(CONFIG.MIN_BATCH_SIZE, damagedPixels.length)); + if (newBatchSize !== state.currentBatchSize) { + state.currentBatchSize = newBatchSize; + Utils.addDebugLog(`Smart batch: Auto-adjusted batch size to ${newBatchSize} to match ${damagedPixels.length} detected damaged pixels`, 'info'); + updateBatchInfo(); // Update UI display + } + } return damagedPixels; } @@ -1665,98 +1821,174 @@ return 0; } - Utils.addDebugLog(`Starting autonomous repair of ${damagedPixels.length} pixels`, 'info'); + Utils.addDebugLog(`Starting enhanced repair of ${damagedPixels.length} pixels with auto-batch (batch size: ${state.currentBatchSize})`, 'info'); updateStatus(Utils.t('repairingPixels', { count: damagedPixels.length })); let repairedCount = 0; let consecutiveFailures = 0; const maxConsecutiveFailures = state.autonomousMode ? 5 : 3; - for (let i = 0; i < damagedPixels.length; i++) { - const pixel = damagedPixels[i]; + // Group pixels by region for batch processing + const pixelsByRegion = new Map(); + + for (const pixel of damagedPixels) { + const absX = state.startPosition.x + pixel.x; + const absY = state.startPosition.y + pixel.y; + const regionX = state.region.x + Math.floor(absX / 1000); + const regionY = state.region.y + Math.floor(absY / 1000); + const regionKey = `${regionX},${regionY}`; + if (!pixelsByRegion.has(regionKey)) { + pixelsByRegion.set(regionKey, []); + } + + pixelsByRegion.get(regionKey).push({ + ...pixel, + regionX, + regionY, + pixelX: absX % 1000, + pixelY: absY % 1000, + color: pixel.isDamagedTransparent && pixel.originalColor.id === 63 ? 63 : pixel.originalColor.id + }); + } + + Utils.addDebugLog(`Grouped ${damagedPixels.length} pixels into ${pixelsByRegion.size} regions for batch processing`, 'info'); + + // Process each region + for (const [regionKey, regionPixels] of pixelsByRegion) { if (state.stopFlag) { Utils.addDebugLog('Repair stopped by user request', 'warning'); break; } - // Enhanced charge management for autonomous mode - await updateCharges(); - let chargeWaitAttempts = 0; - const maxChargeWaitAttempts = state.autonomousMode ? 20 : 10; - - while (state.displayCharges < 1 && !state.stopFlag && chargeWaitAttempts < maxChargeWaitAttempts) { - chargeWaitAttempts++; - const waitTime = state.autonomousMode ? Math.min(state.cooldown, 10000) : state.cooldown; + const [regionX, regionY] = regionKey.split(',').map(Number); + Utils.addDebugLog(`Processing region ${regionKey} with ${regionPixels.length} pixels`, 'info'); + + // Process pixels in batches + for (let i = 0; i < regionPixels.length; i += state.currentBatchSize) { + if (state.stopFlag) break; + + const batch = regionPixels.slice(i, i + state.currentBatchSize); + const actualBatchSize = Math.min(batch.length, state.currentBatchSize); - if (chargeWaitAttempts === 1) { - Utils.addDebugLog(`Waiting for charges... (${state.displayCharges}/${state.maxCharges})`, 'info'); - } - - await Utils.dynamicSleep(() => { - if (state.displayCharges >= 1) return 0; - if (state.stopFlag) return 0; - return waitTime; - }); + // Enhanced charge management for batch processing await updateCharges(); - } - - if (state.stopFlag) break; - - if (state.displayCharges < 1) { - Utils.addDebugLog(`No charges available after waiting, skipping pixel (${pixel.x},${pixel.y})`, 'warning'); - continue; - } - - const success = await repairSinglePixel(pixel); - if (success) { - repairedCount++; - consecutiveFailures = 0; - if (!state.autonomousMode || repairedCount <= 10 || repairedCount % 10 === 0) { - Utils.addDebugLog(`Repaired pixel (${pixel.x},${pixel.y}) with color ${pixel.originalColor.id} [${repairedCount}/${damagedPixels.length}]`, 'success'); - } - } else { - consecutiveFailures++; - Utils.addDebugLog(`Failed to repair pixel (${pixel.x},${pixel.y}) [${consecutiveFailures}/${maxConsecutiveFailures} consecutive failures]`, 'error'); + let chargeWaitAttempts = 0; + const maxChargeWaitAttempts = state.autonomousMode ? 20 : 10; - if (consecutiveFailures >= maxConsecutiveFailures) { - Utils.addDebugLog(`Too many consecutive failures (${consecutiveFailures}), pausing repair`, 'error'); - if (state.autonomousMode) { - Utils.addDebugLog('Autonomous mode: will retry repair in 60 seconds', 'warning'); - setTimeout(() => { - if (state.autoRepairEnabled && !state.stopFlag) { - performRepairCheck(); - } - }, 60000); + while (state.displayCharges < actualBatchSize && !state.stopFlag && chargeWaitAttempts < maxChargeWaitAttempts) { + chargeWaitAttempts++; + const waitTime = state.autonomousMode ? Math.min(state.cooldown, 10000) : state.cooldown; + + if (chargeWaitAttempts === 1) { + Utils.addDebugLog(`Waiting for charges... (need ${actualBatchSize}, have ${state.displayCharges}/${state.maxCharges})`, 'info'); } - break; + + await Utils.dynamicSleep(() => { + if (state.displayCharges >= actualBatchSize) return 0; + if (state.stopFlag) return 0; + return waitTime; + }); + await updateCharges(); } - } - await updateCharges(); + if (state.stopFlag) break; + + if (state.displayCharges < actualBatchSize) { + Utils.addDebugLog(`Insufficient charges for batch (need ${actualBatchSize}, have ${state.displayCharges}), falling back to single pixel repair`, 'warning'); + + // Fall back to single pixel repair + for (const pixel of batch) { + if (state.stopFlag) break; + + await updateCharges(); + if (state.displayCharges < 1) { + Utils.addDebugLog(`No charges available, skipping pixel (${pixel.x},${pixel.y})`, 'warning'); + continue; + } + + const success = await repairSinglePixel(pixel); + if (success) { + repairedCount++; + consecutiveFailures = 0; + } else { + consecutiveFailures++; + } + + if (consecutiveFailures >= maxConsecutiveFailures) break; + await Utils.sleep(100); + } + } else { + // Use batch repair + const batchResult = await repairPixelBatch(regionX, regionY, batch); + + if (batchResult && batchResult.success) { + repairedCount += batchResult.painted; + consecutiveFailures = 0; + + Utils.addDebugLog(`Batch repair: ${batchResult.painted}/${batchResult.total} pixels repaired successfully [Total: ${repairedCount}/${damagedPixels.length}]`, 'success'); + } else { + consecutiveFailures++; + Utils.addDebugLog(`Batch repair failed for ${batch.length} pixels [${consecutiveFailures}/${maxConsecutiveFailures} consecutive failures]`, 'error'); + + if (consecutiveFailures >= maxConsecutiveFailures) { + Utils.addDebugLog(`Too many consecutive batch failures (${consecutiveFailures}), pausing repair`, 'error'); + if (state.autonomousMode) { + Utils.addDebugLog('Autonomous mode: will retry repair in 60 seconds', 'warning'); + setTimeout(() => { + if (state.autoRepairEnabled && !state.stopFlag) { + performRepairCheck(); + } + }, 60000); + } + break; + } + } + } + + await updateCharges(); + + // Dynamic delay based on mode and success rate + const baseDelay = state.autonomousMode ? 100 : 200; + const adaptiveDelay = consecutiveFailures > 0 ? baseDelay * (consecutiveFailures + 1) : baseDelay; + await Utils.sleep(adaptiveDelay); + } - // Dynamic delay based on mode and success rate - const baseDelay = state.autonomousMode ? 100 : 200; - const adaptiveDelay = consecutiveFailures > 0 ? baseDelay * (consecutiveFailures + 1) : baseDelay; - await Utils.sleep(adaptiveDelay); + if (consecutiveFailures >= maxConsecutiveFailures) break; } const message = Utils.t('repairComplete', { repaired: repairedCount }); updateStatus(message); - Utils.addDebugLog(`${message} (${damagedPixels.length - repairedCount} remaining)`, 'success'); + Utils.addDebugLog(`${message} (${damagedPixels.length - repairedCount} remaining) - Used auto-batch with size ${state.currentBatchSize}`, 'success'); return repairedCount; } - async function repairSinglePixel(pixel) { - const { x, y, originalColor } = pixel; + async function repairPixelBatch(regionX, regionY, pixelBatch) { + try { + Utils.addDebugLog(`Attempting batch repair of ${pixelBatch.length} pixels in region ${regionX},${regionY}`, 'info'); + + const result = await WPlaceService.paintPixelBatchInRegion(regionX, regionY, pixelBatch); - const absX = state.startPosition.x + x; - const absY = state.startPosition.y + y; - const regionX = state.region.x + Math.floor(absX / 1000); - const regionY = state.region.y + Math.floor(absY / 1000); - const pixelX = absX % 1000; - const pixelY = absY % 1000; + if (result === 'token_error') { + Utils.addDebugLog('Token error during batch repair, refreshing token...', 'warning'); + await ensureToken(true); + await Utils.sleep(state.autonomousMode ? 500 : 1000); + + // Retry once with new token + const retryResult = await WPlaceService.paintPixelBatchInRegion(regionX, regionY, pixelBatch); + return retryResult; + } + + return result; + } catch (e) { + Utils.addDebugLog(`Error during batch repair: ${e.message}`, 'error'); + return false; + } + } + + async function repairSinglePixel(pixel) { + const { x, y, originalColor, regionX, regionY, pixelX, pixelY, color } = pixel; try { const result = await WPlaceService.paintPixelInRegion( @@ -1764,7 +1996,7 @@ regionY, pixelX, pixelY, - originalColor.id + color || originalColor.id ); if (result === 'token_error') { @@ -1773,10 +2005,16 @@ await Utils.sleep(state.autonomousMode ? 500 : 1000); // Retry once with new token - const retryResult = await WPlaceService.paintPixelInRegion(regionX, regionY, pixelX, pixelY, originalColor.id); + const retryResult = await WPlaceService.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color || originalColor.id); return retryResult === true; } + if (result === true) { + if (pixel.isDamagedTransparent) { + Utils.addDebugLog(`Repaired transparent pixel (${x},${y}) with color ${color || originalColor.id}`, 'success'); + } + } + return result === true; } catch (e) { Utils.addDebugLog(`Error repairing pixel (${x},${y}): ${e.message}`, 'error'); @@ -1993,6 +2231,42 @@ } } + // Update batch information display for new UI + function updateBatchInfo() { + const batchSizeEl = document.getElementById('batchSize'); + const batchStatusEl = document.getElementById('batchStatus'); + const smartModeEl = document.getElementById('smartBatchMode'); + const manualModeEl = document.getElementById('manualBatchMode'); + const manualControlsEl = document.getElementById('manualBatchControls'); + + // Update radio button states + if (smartModeEl && manualModeEl) { + smartModeEl.checked = state.autoBatchEnabled && state.batchOptimization; + manualModeEl.checked = !state.autoBatchEnabled || !state.batchOptimization; + } + + // Update manual controls visibility + if (manualControlsEl) { + manualControlsEl.style.display = (!state.autoBatchEnabled || !state.batchOptimization) ? 'flex' : 'none'; + } + + // Update batch size input + if (batchSizeEl) { + batchSizeEl.value = state.currentBatchSize; + } + + // Update status display + if (batchStatusEl) { + if (state.autoBatchEnabled && state.batchOptimization) { + batchStatusEl.textContent = `🧠 Smart Batch: AUTO (Current: ${state.currentBatchSize})`; + batchStatusEl.className = 'status-info'; + } else { + batchStatusEl.textContent = `✋ Manual Batch: ${state.currentBatchSize} pixels fixed`; + batchStatusEl.className = 'status-warning'; + } + } + } + function updateSystemStatus() { const systemEl = document.getElementById('systemInfo'); if (systemEl) { @@ -2182,6 +2456,149 @@ margin-right: 8px !important; } + /* Enhanced Batch Control Styles */ + .batch-control-group { + display: flex !important; + align-items: center !important; + gap: 8px !important; + margin: 5px 0 !important; + padding: 8px !important; + background: rgba(0, 255, 65, 0.05) !important; + border: 1px solid rgba(0, 255, 65, 0.3) !important; + border-radius: 3px !important; + } + + .batch-control-header { + text-align: center !important; + margin-bottom: 10px !important; + padding: 5px !important; + border-bottom: 1px solid rgba(0, 255, 65, 0.3) !important; + } + + .batch-control-label { + color: #00ff41 !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 6px !important; + text-shadow: 0 0 3px #00ff41 !important; + white-space: nowrap !important; + } + + .batch-mode-selection { + margin: 10px 0 !important; + } + + .batch-mode-option { + margin: 8px 0 !important; + padding: 6px !important; + background: rgba(0, 255, 65, 0.03) !important; + border: 1px solid rgba(0, 255, 65, 0.2) !important; + border-radius: 3px !important; + transition: all 0.3s ease !important; + } + + .batch-mode-option:hover { + background: rgba(0, 255, 65, 0.08) !important; + border-color: rgba(0, 255, 65, 0.4) !important; + } + + .batch-mode-label { + color: #00ff41 !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 6px !important; + text-shadow: 0 0 3px #00ff41 !important; + margin-left: 8px !important; + } + + .neon-radio { + accent-color: #00ff41 !important; + transform: scale(1.2) !important; + margin-right: 8px !important; + } + + .batch-manual-controls { + display: flex !important; + align-items: center !important; + gap: 8px !important; + margin: 8px 0 !important; + padding: 8px !important; + background: rgba(0, 255, 65, 0.05) !important; + border: 1px solid rgba(0, 255, 65, 0.3) !important; + border-radius: 3px !important; + } + + .batch-size-control { + display: flex !important; + align-items: center !important; + gap: 2px !important; + border: 1px solid #00ff41 !important; + border-radius: 3px !important; + background: #16213e !important; + overflow: hidden !important; + } + + .batch-adjust-btn { + width: 20px !important; + height: 24px !important; + background: #16213e !important; + border: none !important; + color: #00ff41 !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 8px !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + text-shadow: 0 0 3px #00ff41 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + } + + .batch-adjust-btn:hover { + background: rgba(0, 255, 65, 0.1) !important; + box-shadow: 0 0 5px rgba(0, 255, 65, 0.3) !important; + } + + .batch-adjust-btn:active { + background: rgba(0, 255, 65, 0.2) !important; + transform: scale(0.95) !important; + } + + .batch-size-input { + width: 50px !important; + height: 24px !important; + background: #16213e !important; + border: none !important; + color: #00ff41 !important; + padding: 0 4px !important; + font-family: 'Press Start 2P', monospace !important; + font-size: 6px !important; + text-align: center !important; + text-shadow: 0 0 3px #00ff41 !important; + outline: none !important; + /* Hide browser default spinner buttons */ + -webkit-appearance: none !important; + -moz-appearance: textfield !important; + } + + /* Hide Chrome spinner buttons */ + .batch-size-input::-webkit-outer-spin-button, + .batch-size-input::-webkit-inner-spin-button { + -webkit-appearance: none !important; + margin: 0 !important; + } + + /* Hide Firefox spinner buttons */ + .batch-size-input[type=number] { + -moz-appearance: textfield !important; + } + + .status-info { + color: #00ff41 !important; + } + + .status-warning { + color: #ff6b35 !important; + } + .neon-panel { background: rgba(22, 33, 62, 0.3) !important; border: 1px solid #00ff41 !important; @@ -2312,6 +2729,9 @@
System: AUTONOMOUS | Token: GENERATOR | Auto-refresh: ON
+
+ Auto-Batch: ON | Size: 5 | Opt: ON +
@@ -2354,11 +2774,54 @@
+ + +
+
+ 🎯 Pixel Batch Mode +
+ + +
+
+ +
+ +
+ +
+
+ + +
+ Batch Size: +
+ + + +
+ pixels +
+
+ +
+
Image: Not loaded
Position: Not set
Colors: 0 available
+
Batch Mode: Auto (${state.currentBatchSize} pixels)
@@ -2414,6 +2877,9 @@ updateSystemStatus(); setInterval(updateSystemStatus, 15000); + // Update batch information + updateBatchInfo(); + // Initialize token generator initializeTokenGenerator(); } @@ -2648,6 +3114,69 @@ } }); + // NEW: Enhanced batch mode controls + document.getElementById('smartBatchMode').addEventListener('change', (e) => { + if (e.target.checked) { + state.autoBatchEnabled = true; + state.batchOptimization = true; + document.getElementById('manualBatchControls').style.display = 'none'; + Utils.addDebugLog('Smart batch mode enabled - will auto-adjust to damage count', 'info'); + updateBatchInfo(); + } + }); + + document.getElementById('manualBatchMode').addEventListener('change', (e) => { + if (e.target.checked) { + state.autoBatchEnabled = false; + state.batchOptimization = false; + document.getElementById('manualBatchControls').style.display = 'flex'; + Utils.addDebugLog('Manual batch mode enabled - using fixed batch size', 'info'); + updateBatchInfo(); + } + }); + + // Manual batch size controls with +/- buttons + document.getElementById('batchSizeIncrease').addEventListener('click', () => { + const newSize = Math.min(CONFIG.MAX_BATCH_SIZE, state.currentBatchSize + 1); + if (newSize !== state.currentBatchSize) { + state.currentBatchSize = newSize; + document.getElementById('batchSize').value = state.currentBatchSize; + Utils.addDebugLog(`Increased batch size to ${state.currentBatchSize} pixels`, 'info'); + updateBatchInfo(); + } + }); + + document.getElementById('batchSizeDecrease').addEventListener('click', () => { + const newSize = Math.max(CONFIG.MIN_BATCH_SIZE, state.currentBatchSize - 1); + if (newSize !== state.currentBatchSize) { + state.currentBatchSize = newSize; + document.getElementById('batchSize').value = state.currentBatchSize; + Utils.addDebugLog(`Decreased batch size to ${state.currentBatchSize} pixels`, 'info'); + updateBatchInfo(); + } + }); + + // Manual input allows direct typing for precise control + document.getElementById('batchSize').addEventListener('input', (e) => { + const value = parseInt(e.target.value); + if (!isNaN(value)) { + const clampedValue = Math.max(CONFIG.MIN_BATCH_SIZE, Math.min(CONFIG.MAX_BATCH_SIZE, value)); + if (clampedValue !== state.currentBatchSize) { + state.currentBatchSize = clampedValue; + if (clampedValue !== value) { + e.target.value = state.currentBatchSize; + } + Utils.addDebugLog(`Manual batch size set to ${state.currentBatchSize} pixels`, 'info'); + updateBatchInfo(); + } + } + }); + + document.getElementById('paintTransparentPixels').addEventListener('change', (e) => { + state.paintTransparentPixels = e.target.checked; + Utils.addDebugLog(`Transparent pixel repair ${state.paintTransparentPixels ? 'enabled' : 'disabled'}`, 'info'); + }); + // Clear debug document.getElementById('clearDebugBtn').addEventListener('click', () => { state.debugLogs = []; diff --git a/Extension/themes/acrylic.css b/Extension/themes/acrylic.css index f94db2a..10995a5 100644 --- a/Extension/themes/acrylic.css +++ b/Extension/themes/acrylic.css @@ -8,6 +8,32 @@ src: url('https://raw.githubusercontent.com/GORAlexComp/AppleColorEmojiFont/refs/heads/main/fonts/AppleColorEmoji.ttf'); } +/* Multi-account panel styling */ +.wplace-theme-acrylic #wplace-accounts-container { + background: var(--wplace-primary); + color: var(--wplace-text); + border-radius: var(--wplace-radius); + box-shadow: none; + backdrop-filter: var(--wplace-backdrop); + border: var(--wplace-border-width) var(--wplace-border-style) + var(--wplace-border-color); + min-width: 250px; +} + +.wplace-theme-acrylic #wplace-accounts-container * { + font-size: 15px; + line-height: normal; +} + +/* Active account styles */ +.wplace-theme-acrylic .wplace-accounts-container .active-account { + --active-bg: color-mix(in srgb, white 15%, transparent); + --active-color: var(--wplace-success); + --inactive-color: var(--wplace-text); + --progress-bg: rgb(255 255 255 / 10%); + --progress-fill: white; +} + /* Main Container */ .wplace-theme-acrylic #wplace-image-bot-container { background: var(--wplace-primary); diff --git a/Extension/themes/classic-light.css b/Extension/themes/classic-light.css index 424ad36..b4d78b1 100644 --- a/Extension/themes/classic-light.css +++ b/Extension/themes/classic-light.css @@ -73,6 +73,26 @@ /* === COMPONENT STYLING === */ +/* Multi-account panel styling */ +.wplace-theme-classic-light #wplace-accounts-container { + background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; + color: var(--wplace-text) !important; + border: 1px solid rgb(0 0 0 / 15%) !important; + border-radius: 12px !important; + box-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%) !important; + font-family: 'Segoe UI', Roboto, sans-serif !important; + backdrop-filter: blur(10px) !important; +} + +/* Active account styles */ +.wplace-theme-classic-light .wplace-accounts-container .active-account { + --active-bg: var(--wplace-secondary) !important; + --active-color: var(--wplace-success) !important; + --inactive-color: var(--wplace-text) !important; + --progress-bg: rgb(0 0 0 / 10%) !important; + --progress-fill: var(--wplace-highlight) !important; +} + /* Main container with clean light styling */ .wplace-theme-classic-light #wplace-image-bot-container { background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important; diff --git a/Extension/themes/classic.css b/Extension/themes/classic.css index 634675e..cd1fb39 100644 --- a/Extension/themes/classic.css +++ b/Extension/themes/classic.css @@ -34,6 +34,29 @@ } /* Classic theme container styling */ +/* Multi-account panel styling */ +:root #wplace-accounts-container, +.wplace-theme-classic #wplace-accounts-container { + 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); +} + +/* Active account styles */ +:root .wplace-accounts-container .active-account, +.wplace-theme-classic .wplace-accounts-container .active-account { + --active-bg: var(--wplace-secondary); + --active-color: var(--wplace-success); + --inactive-color: var(--wplace-text); + --progress-bg: rgb(255 255 255 / 10%); + --progress-fill: var(--wplace-highlight); +} + +/* Base container styling */ :root #wplace-image-bot-container, .wplace-theme-classic #wplace-image-bot-container { background: var(--wplace-primary); diff --git a/Extension/themes/neon.css b/Extension/themes/neon.css index a6d5795..184cb64 100644 --- a/Extension/themes/neon.css +++ b/Extension/themes/neon.css @@ -84,6 +84,122 @@ font-family: var(--wplace-font) !important; } +/* Multi-Account Panel Styles */ +.wplace-theme-neon #wplace-multi-account-container { + position: fixed !important; + top: 180px !important; + right: 20px !important; + width: 320px !important; + background: rgb(22 33 62 / 50%) !important; + border: 2px solid var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important; + font-family: var(--wplace-font) !important; + z-index: 9997 !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; +} + +.wplace-theme-neon #wplace-multi-account-container .wplace-panel-header { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 10px !important; + background: var(--wplace-secondary) !important; + border-bottom: 1px solid var(--wplace-text) !important; + text-transform: uppercase !important; + font-size: 9px !important; +} + +.wplace-theme-neon #wplace-multi-account-container .wplace-header-btn { + background: transparent !important; + border: 1px solid var(--wplace-text) !important; + color: var(--wplace-text) !important; + font-size: 8px !important; + padding: 4px 8px !important; + cursor: pointer !important; + text-shadow: 0 0 5px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; + text-transform: uppercase !important; +} + +.wplace-theme-neon #wplace-multi-account-container .wplace-header-btn:hover { + box-shadow: 0 0 15px var(--wplace-text) !important; + animation: pixel-blink 0.5s infinite !important; +} + +.wplace-theme-neon .wplace-multi-account-content { + padding: 10px !important; +} + +.wplace-theme-neon .wplace-multi-account-info { + font-size: 9px !important; + text-transform: uppercase !important; + margin-bottom: 10px !important; + color: var(--wplace-text) !important; + text-shadow: 0 0 5px var(--wplace-text) !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon .wplace-multi-account-current, +.wplace-theme-neon .wplace-multi-account-stats { + margin-bottom: 8px !important; + font-size: 8px !important; +} + +.wplace-theme-neon .wplace-multi-account-toggle { + margin-bottom: 12px !important; + display: flex !important; + align-items: center !important; +} + +.wplace-theme-neon .wplace-multi-account-switch { + margin-right: 8px !important; +} + +.wplace-theme-neon .wplace-multi-account-list { + max-height: 200px !important; + overflow-y: auto !important; +} + +.wplace-theme-neon .wplace-multi-account-item { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 8px !important; + margin-bottom: 4px !important; + background: rgb(22 33 62 / 50%) !important; + border: 1px solid var(--wplace-text) !important; + font-size: 8px !important; + text-transform: uppercase !important; + font-family: var(--wplace-font) !important; +} + +.wplace-theme-neon .wplace-multi-account-item.active { + background: rgb(0 255 65 / 10%) !important; + border-color: var(--wplace-success) !important; + box-shadow: 0 0 15px var(--wplace-success) !important; +} + +.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar { + width: 6px !important; +} + +.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-track { + background: rgb(22 33 62 / 50%) !important; +} + +.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-thumb { + background: var(--wplace-text) !important; + border-radius: 0 !important; + box-shadow: 0 0 10px var(--wplace-text) !important; +} + +.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-thumb:hover { + background: var(--wplace-success) !important; + box-shadow: 0 0 15px var(--wplace-success) !important; +} + .wplace-theme-neon #wplace-stats-container { background: var(--wplace-primary) !important; border: 2px solid var(--wplace-text) !important; From bada349eb1ba7148e128c757f9d829bc219e8bbe Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 15 Sep 2025 02:58:19 +0800 Subject: [PATCH 16/18] Enhance README formatting and link structure --- README.md | 180 ++++++++++++++++++++++++++---------------------------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 4e90937..780d964 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,120 @@ +WPlace AutoBOT Browser Extension

- image +image

WPlace AutoBOT

-This project was fork from https://github.com/DarkModde/WPlace-AutoBOT +This project was forked from https://github.com/DarkModde/WPlace-AutoBOT

- Consider checking out our discord: https://discord.gg/knkNRYyQcm +Consider checking out our Discord: https://discord.gg/knkNRYyQcm

- Practical scripts with intuitive menus to make your life easier on WPlace!
- Perfect for those who want to level up automatically or build massive pixel arts — without wasting time. - - Warning: This is a purely educational script to demonstrate what can be done on wplace.live, it is not our responsibility of what happens if you use the script. +Practical scripts with intuitive menus to make your life easier on WPlace!
+Perfect for those who want to level up automatically or build massive pixel arts — without wasting time. + +Warning: This is a purely educational script to demonstrate what can be done on wplace.live, it is not our responsibility of what happens if you use the script. +


- If you're wondering—yes! These scripts were Developed by a team consisting of Brazilians, Vietnamese, and Indonesians. HUEHUE! -

- Brazil -   - Vietnam -   - Indonesia +If you're wondering—yes! These scripts were Developed by a team consisting of Brazilians, Vietnamese, and Indonesians. HUEHUE! +

+Brazil +  +Vietnam +  +Indonesia

---- +🚀┃Installation & Usage +Since this is a browser extension that is not on the official web stores, you need to load it manually. Follow the instructions for your browser below. -

🚀┃How to use the scripts:

+For Chrome, Edge, Brave, and other Chromium-based browsers: +Download: Click on the green <> Code button on this GitHub page and select Download ZIP. + +Extract: Unzip the downloaded file (Extension.zip) to a permanent folder you won't delete. + +Open Extensions Page: Open your browser and go to the extensions page. You can usually do this by typing chrome://extensions (for Chrome) or edge://extensions (for Edge) in your address bar. + +Enable Developer Mode: Find and turn on the Developer mode toggle, usually located in the top-right corner of the page. + +Load the Extension: Click on the Load unpacked button that appears. + +Select Folder: In the file dialog, navigate to the folder where you extracted the files and select the main folder (the one containing manifest.json). + +Done! The WPlace AutoBOT extension should now appear in your extensions list and be active. Visit wplace.live and the bot menu will appear. + +For Firefox: +Download: Download the ZIP file from the <> Code button and extract it to a permanent folder, just like in the Chrome instructions. + +Open Debugging Page: Open Firefox and type about:debugging into the address bar and press Enter. + +Select "This Firefox": In the left-hand menu, click on This Firefox. + +Load Temporary Add-on: Click on the Load Temporary Add-on... button. + +Select File: Navigate into the folder you extracted and select the manifest.json file. + +Done! The extension is now loaded for your current browser session. Visit wplace.live to use it. Note: For Firefox, you may need to repeat this process every time you restart the browser. + +For Mobile (Android Only): +You can use the extension on Android using a browser that supports Chrome extensions, such as Kiwi Browser. + +Download and install Kiwi Browser from the Google Play Store. + +Follow the exact same steps listed for Chrome above, using Kiwi Browser instead. The interface for loading unpacked extensions is identical. + +✨┃Features +[x] Automatically farms droplets/levels + +[x] Correct Color Pixel Skip + +[x] Overlay (you don't 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 (via browsers like Kiwi) :P

- It’s super easy: copy one of the codes below, paste it into your browser’s bookmarks bar,
- and run it while you're on wplace.live. +Script Demonstration

-
+📋┃To-do +[x] Making AutoFarm working -### 🎯┃Auto-Farm -#### AUTOFARM USES CHARGES TO GET LEVELS, IT DOES NOT DRAW AN IMAGE FOR YOU. PLEASE USE AUTO-IMAGE FOR THAT -```js -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); })(); -``` +[ ] Merge both scripts into one -### 🖼️┃Auto-Image +[x] Add Overlay system -```js -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); })();; -``` +[x] Add Cross Tiles Support -
-

📖┃Tutorials

+[x] Add AutoCaptcha Solver ---- +[x] Add Correct Color Pixel Skip -![Parte 1](https://i.imgur.com/yneG5if.png) +[x] Separate Css ---- +Contributors: -![Parte 2](https://i.imgur.com/ZRpU0wZ.png) - ---- - -![Parte 3](https://i.imgur.com/lfjfcEw.png) - -
- - -
- -> [!IMPORTANT] ->

1. None of the scripts will work if you close the browser tab. You must keep the WPlace tab open, even if it's in the background.

->

2. Never run both scripts on the same page — this can bug your WPlace. If it happens, just refresh the page!

->

3. If the script doesn’t detect the available colors, just click the "Paint" button to show them — only then start the Auto-Imager.

- -
- -### ✨┃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 - - ---- - - ---- +Contributors

- Script Demonstration -

- ---- - -### 📋┃To-do - -- [x] Making AutoFarm working -- [ ] 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: - Contributors - - -

- +

From 7c6236704918e83c03685781f599a397806cd1a4 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 15 Sep 2025 03:00:37 +0800 Subject: [PATCH 17/18] Revise README installation instructions --- README.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 780d964..9fb61ea 100644 --- a/README.md +++ b/README.md @@ -34,40 +34,39 @@ If you're wondering—yes! These scripts were Developed by a team consisting of 🚀┃Installation & Usage Since this is a browser extension that is not on the official web stores, you need to load it manually. Follow the instructions for your browser below. -For Chrome, Edge, Brave, and other Chromium-based browsers: -Download: Click on the green <> Code button on this GitHub page and select Download ZIP. +> Download From here https://github.com/Wplace-AutoBot/WPlace-AutoBOT/releases/tag/Extension -Extract: Unzip the downloaded file (Extension.zip) to a permanent folder you won't delete. +>Extract: Unzip the downloaded file (Extension.zip) to a permanent folder you won't delete. -Open Extensions Page: Open your browser and go to the extensions page. You can usually do this by typing chrome://extensions (for Chrome) or edge://extensions (for Edge) in your address bar. +>Open Extensions Page: Open your browser and go to the extensions page. You can usually do this by typing chrome://extensions (for Chrome) or edge://extensions (for Edge) in your address bar. -Enable Developer Mode: Find and turn on the Developer mode toggle, usually located in the top-right corner of the page. +>Enable Developer Mode: Find and turn on the Developer mode toggle, usually located in the top-right corner of the page. -Load the Extension: Click on the Load unpacked button that appears. +>Load the Extension: Click on the Load unpacked button that appears. -Select Folder: In the file dialog, navigate to the folder where you extracted the files and select the main folder (the one containing manifest.json). +>Select Folder: In the file dialog, navigate to the folder where you extracted the files and select the main folder (the one containing manifest.json). -Done! The WPlace AutoBOT extension should now appear in your extensions list and be active. Visit wplace.live and the bot menu will appear. +>Done! The WPlace AutoBOT extension should now appear in your extensions list and be active. Visit wplace.live and the bot menu will appear. For Firefox: -Download: Download the ZIP file from the <> Code button and extract it to a permanent folder, just like in the Chrome instructions. +> Download https://github.com/Wplace-AutoBot/WPlace-AutoBOT/releases/tag/Extension -Open Debugging Page: Open Firefox and type about:debugging into the address bar and press Enter. +>Open Debugging Page: Open Firefox and type about:debugging into the address bar and press Enter. -Select "This Firefox": In the left-hand menu, click on This Firefox. +>Select "This Firefox": In the left-hand menu, click on This Firefox. -Load Temporary Add-on: Click on the Load Temporary Add-on... button. +>Load Temporary Add-on: Click on the Load Temporary Add-on... button. -Select File: Navigate into the folder you extracted and select the manifest.json file. +>Select File: Navigate into the folder you extracted and select the manifest.json file. -Done! The extension is now loaded for your current browser session. Visit wplace.live to use it. Note: For Firefox, you may need to repeat this process every time you restart the browser. +>Done! The extension is now loaded for your current browser session. Visit wplace.live to use it. Note: For Firefox, you may need to repeat this process every time you restart the browser. For Mobile (Android Only): You can use the extension on Android using a browser that supports Chrome extensions, such as Kiwi Browser. -Download and install Kiwi Browser from the Google Play Store. +>Download and install Kiwi Browser from the Google Play Store. -Follow the exact same steps listed for Chrome above, using Kiwi Browser instead. The interface for loading unpacked extensions is identical. +>Follow the exact same steps listed for Chrome above, using Kiwi Browser instead. The interface for loading unpacked extensions is identical. ✨┃Features [x] Automatically farms droplets/levels From 0787d0809f761538786bd77a69077bc6a28dac6f Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Mon, 15 Sep 2025 03:16:13 +0800 Subject: [PATCH 18/18] Update README.md --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9fb61ea..7abb640 100644 --- a/README.md +++ b/README.md @@ -69,27 +69,27 @@ You can use the extension on Android using a browser that supports Chrome extens >Follow the exact same steps listed for Chrome above, using Kiwi Browser instead. The interface for loading unpacked extensions is identical. ✨┃Features -[x] Automatically farms droplets/levels +- [x] Automatically farms droplets/levels -[x] Correct Color Pixel Skip +- [x] Correct Color Pixel Skip -[x] Overlay (you don't need Bluemarble anymore) +- [x] Overlay (you don't need Bluemarble anymore) -[x] Paint over multiple tiles +- [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] 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] AutoCaptcha Solver With Turnstile Token Generator -[x] Wider Paint Control Options +- [x] Wider Paint Control Options -[x] Advance Color Processing +- [x] Advance Color Processing -[x] MultiLanguage Support +- [x] MultiLanguage Support -[x] Multi account through save and load feature +- [x] Multi account through save and load feature -[x] Works on mobile (via browsers like Kiwi) :P +- [x] Works on mobile (via browsers like Kiwi) :P

Script Demonstration @@ -98,17 +98,17 @@ You can use the extension on Android using a browser that supports Chrome extens 📋┃To-do [x] Making AutoFarm working -[ ] Merge both scripts into one +- [ ] Merge both scripts into one -[x] Add Overlay system +- [x] Add Overlay system -[x] Add Cross Tiles Support +- [x] Add Cross Tiles Support -[x] Add AutoCaptcha Solver +- [x] Add AutoCaptcha Solver -[x] Add Correct Color Pixel Skip +- [x] Add Correct Color Pixel Skip -[x] Separate Css +- [x] Separate Css Contributors: