From 204afcd178d711169a8f22f96a65f1b21fd73d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=A9ng?= <99890979+maiminhdung@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:26:25 +0700 Subject: [PATCH] NEW UPDATE + Add Setting UI + Add Change paint pixel per second + Optimize Memory Usage + Optimize Heavy DOM + Optimize Color Matching Algorithm + Optimize Batch Processing + Optimize Memory Management - Cleanup Unused Variables --- Auto-Image.js | 1275 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 1122 insertions(+), 153 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index 7c438d9..f261714 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -5,6 +5,35 @@ TRANSPARENCY_THRESHOLD: 100, WHITE_THRESHOLD: 250, LOG_INTERVAL: 10, + PAINTING_SPEED: { + MIN: 1, // Minimum 1 pixel per second + MAX: 1000, // Maximum 1000 pixels per second + DEFAULT: 5, // Default 5 pixels per second + }, + // 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", @@ -68,12 +97,7 @@ existingStyle.remove() } - // Recreate UI with new theme - const existingContainer = document.getElementById("wplace-image-bot-container") - const existingStats = document.getElementById("wplace-stats-container") - if (existingContainer) existingContainer.remove() - if (existingStats) existingStats.remove() - + // Recreate UI with new theme (cleanup is handled in createUI) createUI() } } @@ -97,6 +121,17 @@ } } + const loadLanguagePreference = () => { + try { + const saved = localStorage.getItem("wplace_language") + if (saved && TEXT[saved]) { + state.language = saved + } + } catch (e) { + console.warn("Could not load language preference:", e) + } + } + // BILINGUAL TEXT STRINGS const TEXT = { en: { @@ -148,6 +183,16 @@ 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.", }, pt: { title: "WPlace Auto-Image", @@ -198,6 +243,136 @@ 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.", + }, + vi: { + title: "WPlace Auto-Image", + initBot: "Khởi động Auto-BOT", + 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ắc có sẵ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 'Khởi động Auto-BOT' để bắt đầu", + waitingInit: "Đang chờ khởi tạo...", + 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: "❗ Cần token CAPTCHA. Vẽ một pixel thủ công để tiếp tục.", + 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.", + }, + fr: { + title: "WPlace Auto-Image", + initBot: "Démarrer Auto-BOT", + 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 disponibles trouvées", + 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 'Démarrer Auto-BOT' pour commencer", + waitingInit: "En attente d'initialisation...", + resizeSuccess: "✅ Image redimensionnée en {width}x{height}", + paintingPaused: "⏸️ Peinture en pause à la position X: {x}, Y: {y}", + captchaNeeded: "❗ Token CAPTCHA nécessaire. Peignez un pixel manuellement pour continuer.", + 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.", }, } @@ -221,6 +396,7 @@ lastPosition: { x: 0, y: 0 }, estimatedTime: 0, language: "en", + paintingSpeed: CONFIG.PAINTING_SPEED.DEFAULT, // pixels per second } // Global variable to store the captured CAPTCHA token. @@ -272,6 +448,50 @@ const Utils = { sleep: (ms) => new Promise((r) => setTimeout(r, ms)), + // Optimized DOM creation helpers + createElement: (tag, props = {}, children = []) => { + const element = document.createElement(tag) + + // Set properties efficiently + 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) + } + }) + + // Append children efficiently + 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 + }, + + // Create button with common styling + 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 + }, + t: (key, params = {}) => { let text = TEXT[state.language]?.[key] || TEXT.en[key] || key Object.keys(params).forEach((param) => { @@ -418,8 +638,17 @@ calculateEstimatedTime: (remainingPixels, charges, cooldown) => { if (remainingPixels <= 0) return 0 + + // Calculate time based on painting speed (pixels per second) + const paintingSpeedDelay = state.paintingSpeed > 0 ? (1000 / state.paintingSpeed) : 1000 + const timeFromSpeed = remainingPixels * paintingSpeedDelay // ms + + // Calculate time based on charges and cooldown const cyclesNeeded = Math.ceil(remainingPixels / Math.max(charges, 1)) - return cyclesNeeded * cooldown + const timeFromCharges = cyclesNeeded * cooldown // ms + + // Return the maximum of both calculations (the limiting factor) + return Math.max(timeFromSpeed, timeFromCharges) }, // Save/Load Progress Functions @@ -674,19 +903,43 @@ }, } - // COLOR MATCHING FUNCTION + // COLOR MATCHING FUNCTION - Optimized with caching + const colorCache = new Map() + function findClosestColor(targetRgb, availableColors) { + // Create cache key from RGB values + const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}` + + // Check cache first + if (colorCache.has(cacheKey)) { + return colorCache.get(cacheKey) + } + let minDistance = Number.POSITIVE_INFINITY let closestColorId = availableColors[0]?.id || 1 - for (const color of availableColors) { + // Use optimized loop for better performance + for (let i = 0; i < availableColors.length; i++) { + const color = availableColors[i] const distance = Utils.colorDistance(targetRgb, color.rgb) if (distance < minDistance) { minDistance = distance closestColorId = color.id + + // If perfect match, break early + if (distance === 0) break } } + // Cache the result for future use + colorCache.set(cacheKey, closestColorId) + + // Limit cache size to prevent memory leaks + if (colorCache.size > 10000) { + const firstKey = colorCache.keys().next().value + colorCache.delete(firstKey) + } + return closestColorId } @@ -695,84 +948,24 @@ let updateStats = () => {} let updateDataButtons = () => {} - const createThemePopup = () => { - // Remove existing popup if it exists - const existingPopup = document.getElementById("theme-popup") - if (existingPopup) { - existingPopup.remove() - return - } - - const popup = document.createElement("div") - popup.id = "theme-popup" - popup.style.cssText = ` - position: fixed; - top: 60px; - right: 20px; - background: ${getCurrentTheme().secondary}; - border: ${getCurrentTheme().borderWidth} ${getCurrentTheme().borderStyle} ${getCurrentTheme().accent}; - border-radius: ${getCurrentTheme().borderRadius}; - box-shadow: ${getCurrentTheme().boxShadow}; - backdrop-filter: ${getCurrentTheme().backdropFilter}; - z-index: 10001; - min-width: 200px; - padding: 10px 0; - font-family: ${getCurrentTheme().fontFamily}; - ` - - Object.keys(CONFIG.THEMES).forEach((themeName) => { - const themeOption = document.createElement("div") - themeOption.style.cssText = ` - padding: 10px 20px; - color: ${themeName === CONFIG.currentTheme ? getCurrentTheme().highlight : getCurrentTheme().text}; - cursor: pointer; - font-size: 12px; - transition: all 0.2s ease; - background: ${themeName === CONFIG.currentTheme ? getCurrentTheme().accent : "transparent"}; - ` - themeOption.textContent = themeName - - themeOption.addEventListener("mouseenter", () => { - if (themeName !== CONFIG.currentTheme) { - themeOption.style.background = getCurrentTheme().accent - themeOption.style.color = getCurrentTheme().highlight - } - }) - - themeOption.addEventListener("mouseleave", () => { - if (themeName !== CONFIG.currentTheme) { - themeOption.style.background = "transparent" - themeOption.style.color = getCurrentTheme().text - } - }) - - themeOption.addEventListener("click", () => { - switchTheme(themeName) - popup.remove() - }) - - popup.appendChild(themeOption) - }) - - document.body.appendChild(popup) - - // Close popup when clicking outside - const closePopup = (e) => { - if (!popup.contains(e.target) && !e.target.closest("#themeBtn")) { - popup.remove() - document.removeEventListener("click", closePopup) - } - } - - setTimeout(() => { - document.addEventListener("click", closePopup) - }, 100) - } - async function createUI() { await detectLanguage() + // Clean up existing UI elements to prevent duplicates + 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() + loadLanguagePreference() const theme = getCurrentTheme() @@ -844,7 +1037,7 @@ position: fixed; top: 20px; right: 20px; - width: ${CONFIG.currentTheme === "Neon Retro" ? "320px" : "280px"}; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; max-height: calc(100vh - 40px); background: ${ CONFIG.currentTheme === "Classic Autobot" @@ -927,7 +1120,7 @@ position: fixed; top: 20px; left: 20px; - width: ${CONFIG.currentTheme === "Neon Retro" ? "320px" : "280px"}; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; max-height: calc(100vh - 40px); background: ${ CONFIG.currentTheme === "Classic Autobot" @@ -950,14 +1143,14 @@ } .wplace-header { - padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px 15px" : "8px 12px"}; + padding: ${CONFIG.currentTheme === "Neon Retro" ? "8px 12px" : "8px 12px"}; background: ${ CONFIG.currentTheme === "Classic Autobot" ? `linear-gradient(135deg, ${theme.secondary} 0%, #2a2a2a 100%)` : theme.secondary }; color: ${theme.highlight}; - font-size: ${CONFIG.currentTheme === "Neon Retro" ? "10px" : "13px"}; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "11px" : "13px"}; font-weight: ${CONFIG.currentTheme === "Neon Retro" ? "normal" : "700"}; display: flex; justify-content: space-between; @@ -975,12 +1168,12 @@ .wplace-header-title { display: flex; align-items: center; - gap: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "6px"}; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; } .wplace-header-controls { display: flex; - gap: ${CONFIG.currentTheme === "Neon Retro" ? "10px" : "6px"}; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; } .wplace-header-btn { @@ -1008,7 +1201,7 @@ } .wplace-content { - padding: ${CONFIG.currentTheme === "Neon Retro" ? "15px" : "12px"}; + padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; display: block; position: relative; z-index: 2; @@ -1026,7 +1219,7 @@ } .wplace-section { - margin-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "15px" : "12px"}; + margin-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; padding: 12px; background: rgba(255,255,255,0.03); border-radius: ${theme.borderRadius}; @@ -1464,6 +1657,326 @@ /* Icon styling for retro feel */ .fas, .fa { filter: drop-shadow(0 0 3px currentColor); + } + + /* Speed Control Styles */ + .wplace-speed-control { + margin-top: 12px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-speed-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-speed-label i { + margin-right: 6px; + color: ${theme.highlight}; + } + + .wplace-speed-slider-container { + display: flex; + align-items: center; + gap: 12px; + } + + .wplace-speed-slider { + flex: 1; + height: 6px; + border-radius: 3px; + background: ${theme.primary}; + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .wplace-speed-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-slider::-moz-range-thumb { + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-display { + display: flex; + align-items: center; + gap: 4px; + min-width: 90px; + justify-content: flex-end; + } + + #speedValue { + color: ${theme.highlight}; + font-weight: 600; + font-size: 14px; + } + + .wplace-speed-unit { + color: ${theme.text}; + font-size: 11px; + opacity: 0.8; + } + + /* Settings Window Styles */ + #wplace-settings-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10001; + min-width: 400px; + max-width: 500px; + background: ${theme.primary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + box-shadow: ${theme.boxShadow}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-settings { + padding: 16px; + max-height: 400px; + overflow-y: auto; + } + + .wplace-setting-section { + margin-bottom: 20px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-setting-title { + display: flex; + align-items: center; + margin-bottom: 12px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-setting-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-setting-content { + color: ${theme.text}; + } + + .wplace-section { + margin-bottom: 20px; + padding: 15px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-section-title { + display: flex; + align-items: center; + margin-bottom: 15px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-section-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-speed-container { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 10px; + } + + .wplace-slider { + flex: 1; + height: 6px; + background: ${theme.accent}; + border-radius: 3px; + outline: none; + -webkit-appearance: none; + } + + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 18px; + height: 18px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + border: 2px solid ${theme.primary}; + } + + .wplace-speed-display { + background: ${theme.accent}; + padding: 5px 10px; + border-radius: 4px; + color: ${theme.text}; + font-weight: 600; + min-width: 80px; + text-align: center; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-select { + width: 100%; + padding: 8px 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + color: ${theme.text}; + font-size: 14px; + margin-bottom: 10px; + } + + .wplace-select:focus { + outline: none; + border-color: ${theme.highlight}; + } + + .wplace-description { + color: ${theme.text}; + font-size: 12px; + opacity: 0.8; + line-height: 1.4; + } + + .wplace-theme-custom { + margin-top: 15px; + padding: 15px; + background: ${theme.accent}; + border-radius: ${theme.borderRadius}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-custom-group { + margin-bottom: 15px; + } + + .wplace-custom-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-custom-label i { + margin-right: 8px; + color: ${theme.highlight}; + width: 16px; + } + + .wplace-color-input-group { + display: flex; + gap: 8px; + align-items: center; + } + + .wplace-color-input { + width: 50px; + height: 30px; + border: none; + border-radius: 4px; + cursor: pointer; + background: transparent; + } + + .wplace-color-text { + flex: 1; + padding: 6px 10px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: 4px; + color: ${theme.text}; + font-size: 12px; + font-family: monospace; + } + + .wplace-animation-controls { + display: flex; + flex-direction: column; + gap: 8px; + } + + .wplace-checkbox-label { + display: flex; + align-items: center; + gap: 8px; + color: ${theme.text}; + font-size: 12px; + cursor: pointer; + } + + .wplace-checkbox-label input[type="checkbox"] { + accent-color: ${theme.highlight}; + } + + .wplace-slider-container { + display: flex; + align-items: center; + gap: 10px; + } + + .wplace-slider-container .wplace-slider { + flex: 1; + } + + .wplace-slider-container span { + color: ${theme.text}; + font-size: 12px; + font-weight: 600; + min-width: 40px; + } + + .wplace-custom-actions { + display: flex; + gap: 10px; + margin-top: 20px; + border-top: 1px solid ${theme.accent}; + padding-top: 15px; + } + + .wplace-btn-secondary { + background: ${theme.accent}; + color: ${theme.text}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-btn-secondary:hover { + background: ${theme.secondary}; }` : "" } @@ -1479,8 +1992,8 @@ ${Utils.t("title")}