Smart Grammar Fixer Pro

Smart grammar fixing with LanguageTool and language detection

目前為 2025-11-22 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Smart Grammar Fixer Pro
// @namespace    http://tampermonkey.net/
// @version      6.1
// @description  Smart grammar fixing with LanguageTool and language detection
// @author       You
// @match        *://*/*
// @exclude      https://docs.google.com/*
// @exclude      https://*.office.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        unsafeWindow
// @connect      api.languagetool.org
// @connect      ws.detectlanguage.com
// @antifeature  none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // API Configuration
    const API_CONFIG = {
        languagetool: 'https://api.languagetool.org/v2/check',
        detectlanguage: 'https://ws.detectlanguage.com/0.2/detect'
    };

    // Language Support
    const LANGUAGE_SUPPORT = {
        'en-US': 'English (US)',
        'en-GB': 'English (GB)',
        'ar': 'Arabic',
        'de-DE': 'German',
        'fr-FR': 'French',
        'es-ES': 'Spanish',
        'it-IT': 'Italian',
        'pt-PT': 'Portuguese',
        'pt-BR': 'Portuguese (BR)',
        'nl-NL': 'Dutch',
        'ru-RU': 'Russian',
        'ja-JP': 'Japanese',
        'zh-CN': 'Chinese',
        'ko-KR': 'Korean',
        'pl-PL': 'Polish',
        'sv-SE': 'Swedish',
        'da-DK': 'Danish',
        'fi-FI': 'Finnish',
        'no-NO': 'Norwegian',
        'tr-TR': 'Turkish'
    };

    // Default settings
    const DEFAULT_SETTINGS = {
        // Core Behavior
        enabled: true,
        debugMode: false,

        // API Keys
        apiKeys: {
            detectlanguage: 'a40f80d21131976bdedf653088a12ce0'
        },

        // Language Configuration
        language: {
            main: 'en-US',
            fallback: 'en-US',
            autoDetect: true,
            confidenceThreshold: 0.7,
            forceLanguage: false,
            correctionLanguage: 'auto' // New: Choose language for correction
        },

        // Correction Settings
        correction: {
            autoFixOnSend: true,
            minTextLength: 3,
            maxTextLength: 5000,
            fixPunctuation: true,
            fixCapitalization: true,
            aggressiveCorrection: false
        },

        // User Interface
        ui: {
            showIcons: true,
            showNotifications: true,
            iconPosition: 'top-right',
            iconSize: 'medium',
            darkMode: 'auto',
            animations: true // New: Enable animations
        },

        // Shortcuts
        shortcuts: {
            smartFix: 'Alt+A',
            fixAndSend: 'Alt+Enter',
            quickFix: 'Alt+Q',
            toggleEnabled: 'Alt+Shift+G',
            openSettings: 'Alt+Shift+S'
        },

        // Domain-specific Rules
        domainRules: {
            'gmail.com': { autoFixOnSend: true },
            'outlook.com': { autoFixOnSend: true },
            'twitter.com': { minTextLength: 5 },
            'facebook.com': { minTextLength: 10 },
            'chat.openai.com': { enabled: false },
            'docs.google.com': { enabled: false }
        }
    };

    let settings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
    let isProcessing = false;
    let currentDomain = window.location.hostname;

    // Inject styles
    function injectStyles() {
        const styles = `
            .grammar-helper-icon {
                position: absolute;
                width: 24px;
                height: 24px;
                background: #4CAF50;
                color: white;
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 11px;
                font-weight: bold;
                cursor: pointer;
                z-index: 10000;
                box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                transition: all 0.3s ease;
                border: 2px solid white;
            }
            .grammar-helper-icon:hover {
                transform: scale(1.2);
                box-shadow: 0 4px 12px rgba(0,0,0,0.4);
            }
            .grammar-language-badge {
                position: absolute;
                top: -5px;
                right: -5px;
                background: #2196F3;
                color: white;
                border-radius: 50%;
                width: 16px;
                height: 16px;
                font-size: 9px;
                display: flex;
                align-items: center;
                justify-content: center;
                border: 1px solid white;
                font-weight: bold;
            }

            /* Enhanced notification animations */
            .grammar-notification {
                position: absolute;
                background: #4CAF50;
                color: white;
                padding: 8px 12px;
                border-radius: 6px;
                font-size: 12px;
                font-family: Arial, sans-serif;
                z-index: 10002;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                max-width: 200px;
                white-space: nowrap;
                transform: translateY(-20px);
                opacity: 0;
            }
            .grammar-notification.error {
                background: #f44336;
            }
            .grammar-notification.warning {
                background: #FF9800;
            }
            .grammar-notification.info {
                background: #2196F3;
            }
            .grammar-notification.processing {
                background: #9C27B0;
            }
            .grammar-notification.success {
                background: #4CAF50;
            }

            /* Animation classes */
            .grammar-notification.show {
                animation: slideInBounce 0.5s ease-out forwards;
            }
            .grammar-notification.hide {
                animation: slideOutUp 0.3s ease-in forwards;
            }

            /* Processing animation */
            .grammar-notification.processing::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                height: 3px;
                background: rgba(255,255,255,0.7);
                border-radius: 0 0 6px 6px;
                animation: processingBar 2s linear infinite;
            }

            @keyframes slideInBounce {
                0% {
                    transform: translateY(-20px);
                    opacity: 0;
                }
                60% {
                    transform: translateY(5px);
                    opacity: 1;
                }
                100% {
                    transform: translateY(0);
                    opacity: 1;
                }
            }

            @keyframes slideOutUp {
                0% {
                    transform: translateY(0);
                    opacity: 1;
                }
                100% {
                    transform: translateY(-20px);
                    opacity: 0;
                }
            }

            @keyframes processingBar {
                0% {
                    width: 0%;
                }
                50% {
                    width: 100%;
                }
                100% {
                    width: 0%;
                }
            }

            /* Pulse animation for icon when processing */
            .grammar-helper-icon.processing {
                animation: pulse 1.5s ease-in-out infinite;
            }

            @keyframes pulse {
                0% {
                    transform: scale(1);
                    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                }
                50% {
                    transform: scale(1.1);
                    box-shadow: 0 4px 16px rgba(76, 175, 80, 0.4);
                }
                100% {
                    transform: scale(1);
                    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                }
            }

            .grammar-global-notification {
                position: fixed;
                top: 20px;
                right: 20px;
                background: #2196F3;
                color: white;
                padding: 12px 16px;
                border-radius: 8px;
                z-index: 100000;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                font-family: Arial, sans-serif;
                font-size: 14px;
                max-width: 400px;
                word-wrap: break-word;
                transform: translateX(100%);
                transition: transform 0.3s ease;
            }
            .grammar-global-notification.show {
                transform: translateX(0);
            }
            .grammar-global-notification.success {
                background: #4CAF50;
            }
            .grammar-global-notification.error {
                background: #f44336;
            }
            .grammar-global-notification.warning {
                background: #FF9800;
            }
        `;

        const styleSheet = document.createElement('style');
        styleSheet.textContent = styles;
        document.head.appendChild(styleSheet);

        // Inject settings panel styles
        injectSettingsPanelStyles();
    }

    function injectSettingsPanelStyles() {
        const settingsStyles = `
        .grammar-settings-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.7);
            z-index: 99998;
            backdrop-filter: blur(5px);
            animation: fadeIn 0.3s ease;
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        .grammar-settings-panel {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%) scale(0.9);
            background: white;
            border-radius: 12px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            z-index: 99999;
            width: 90%;
            max-width: 700px;
            max-height: 90vh;
            overflow: hidden;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            animation: scaleIn 0.3s ease forwards;
        }

        @keyframes scaleIn {
            to {
                transform: translate(-50%, -50%) scale(1);
            }
        }

        .grammar-settings-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .grammar-settings-title h2 {
            margin: 0;
            font-size: 1.5em;
            font-weight: 600;
        }

        .grammar-settings-subtitle {
            opacity: 0.9;
            font-size: 0.9em;
            margin-top: 4px;
        }

        .grammar-settings-close {
            background: rgba(255, 255, 255, 0.2);
            border: none;
            color: white;
            font-size: 24px;
            cursor: pointer;
            padding: 0;
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background 0.2s ease;
        }

        .grammar-settings-close:hover {
            background: rgba(255, 255, 255, 0.3);
        }

        .grammar-settings-content {
            padding: 24px;
            max-height: 60vh;
            overflow-y: auto;
        }

        .settings-section {
            margin-bottom: 24px;
            animation: slideDown 0.4s ease;
        }

        @keyframes slideDown {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .settings-section h3 {
            margin: 0 0 16px 0;
            font-size: 1.1em;
            color: #495057;
            border-bottom: 1px solid #e9ecef;
            padding-bottom: 8px;
        }

        .settings-row {
            display: flex;
            align-items: center;
            margin-bottom: 12px;
            padding: 8px 0;
        }

        .settings-row label {
            flex: 1;
            margin-right: 16px;
            font-size: 14px;
            color: #495057;
        }

        .settings-row input[type="text"],
        .settings-row input[type="password"],
        .settings-row input[type="number"],
        .settings-row select {
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 14px;
            min-width: 200px;
            transition: border-color 0.2s ease;
        }

        .settings-row input:focus,
        .settings-row select:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
        }

        .settings-row input[type="checkbox"] {
            margin-right: 8px;
            transform: scale(1.1);
        }

        .shortcut-input {
            background: #f8f9fa !important;
            cursor: pointer;
            min-width: 120px !important;
            text-align: center;
            font-family: monospace;
        }

        .shortcut-input.recording {
            background: #fff3cd !important;
            border-color: #ffc107;
            color: #856404;
        }

        .grammar-settings-footer {
            padding: 20px 24px;
            background: #f8f9fa;
            border-top: 1px solid #e9ecef;
            display: flex;
            justify-content: flex-end;
            gap: 12px;
        }

        .grammar-settings-btn {
            padding: 10px 20px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: all 0.2s ease;
        }

        .grammar-settings-btn.primary {
            background: #667eea;
            color: white;
        }

        .grammar-settings-btn.primary:hover {
            background: #5a6fd8;
            transform: translateY(-1px);
        }

        .grammar-settings-btn.secondary {
            background: #6c757d;
            color: white;
        }

        .grammar-settings-btn.secondary:hover {
            background: #5a6268;
            transform: translateY(-1px);
        }

        @media (max-width: 768px) {
            .grammar-settings-panel {
                width: 95%;
                height: 95vh;
            }

            .settings-row {
                flex-direction: column;
                align-items: flex-start;
            }

            .settings-row label {
                margin-bottom: 8px;
                margin-right: 0;
            }
        }
        `;

        const styleSheet = document.createElement('style');
        styleSheet.textContent = settingsStyles;
        document.head.appendChild(styleSheet);
    }

    // Initialize the script
    async function init() {
        try {
            console.log('🔄 Starting Grammar Fixer initialization...');
            await loadSettings();
            applyDomainSpecificRules();
            injectStyles();
            setupGlobalShortcuts();
            setupElementObservers();
            registerMenuCommands();

            console.log('✅ Smart Grammar Fixer Pro initialized successfully');
            console.log('🌐 Current domain:', currentDomain);

            showGlobalNotification('Grammar Fixer Pro ready! Use ' + settings.shortcuts.smartFix + ' to fix grammar.', 'success', 3000);

        } catch (error) {
            console.error('❌ Failed to initialize grammar fixer:', error);
            showGlobalNotification('Grammar fixer failed to initialize', 'error');
        }
    }

    function applyDomainSpecificRules() {
        const domainRule = settings.domainRules[currentDomain];
        if (domainRule) {
            Object.assign(settings, domainRule);
            if (settings.debugMode) {
                console.log('🔧 Applied domain-specific rules for:', currentDomain, domainRule);
            }
        }
    }

    // Settings management
    async function loadSettings() {
        try {
            const savedSettings = await GM_getValue('grammarSettings');
            if (savedSettings) {
                settings = Object.assign({}, DEFAULT_SETTINGS, savedSettings);
            }
        } catch (error) {
            console.error('Error loading settings:', error);
            settings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
        }
    }

    async function saveSettings() {
        try {
            await GM_setValue('grammarSettings', settings);
            return true;
        } catch (error) {
            console.error('Error saving settings:', error);
            showGlobalNotification('Failed to save settings', 'error');
            return false;
        }
    }

    // Menu commands
    function registerMenuCommands() {
        GM_registerMenuCommand('⚙️ Grammar Settings', showSettingsPanel);
        GM_registerMenuCommand('🔄 Toggle Enabled', toggleEnabled);
        GM_registerMenuCommand('📊 Status Info', showStatusInfo);
    }

    function toggleEnabled() {
        settings.enabled = !settings.enabled;
        saveSettings();
        showGlobalNotification(`Grammar fixer ${settings.enabled ? 'enabled' : 'disabled'}`);

        if (!settings.enabled) {
            removeAllUIElements();
        } else {
            setTimeout(() => {
                document.querySelectorAll('textarea, input[type="text"], [contenteditable="true"]')
                    .forEach(addSmartIconsToElement);
            }, 1000);
        }
    }

    function showStatusInfo() {
        const status = `
Status: ${settings.enabled ? '✅ Enabled' : '❌ Disabled'}
Domain: ${currentDomain}
Main Language: ${settings.language.main}
Correction Language: ${settings.language.correctionLanguage}
Auto-detect: ${settings.language.autoDetect ? '✅' : '❌'}
Shortcuts: ${Object.values(settings.shortcuts).join(', ')}
        `.trim();

        showGlobalNotification(status, 'info', 5000);
    }

    // Shortcuts
    function setupGlobalShortcuts() {
        document.addEventListener('keydown', function(e) {
            if (!settings.enabled) return;

            const activeEl = document.activeElement;
            if (!isTextElement(activeEl)) return;

            // Smart Fix
            if (checkShortcut(e, settings.shortcuts.smartFix)) {
                e.preventDefault();
                e.stopPropagation();
                handleSmartGrammarFix(activeEl);
            }

            // Fix and Send
            if (checkShortcut(e, settings.shortcuts.fixAndSend)) {
                e.preventDefault();
                e.stopPropagation();
                handleFixAndSend(activeEl);
            }

            // Quick Fix
            if (checkShortcut(e, settings.shortcuts.quickFix)) {
                e.preventDefault();
                e.stopPropagation();
                handleQuickFix(activeEl);
            }

            // Toggle Enabled
            if (checkShortcut(e, settings.shortcuts.toggleEnabled)) {
                e.preventDefault();
                e.stopPropagation();
                toggleEnabled();
            }

            // Open Settings
            if (checkShortcut(e, settings.shortcuts.openSettings)) {
                e.preventDefault();
                e.stopPropagation();
                showSettingsPanel();
            }
        }, true);
    }

    function checkShortcut(event, shortcut) {
        const keys = shortcut.split('+');
        let match = true;

        keys.forEach(key => {
            key = key.trim().toLowerCase();
            if (key === 'alt' && !event.altKey) match = false;
            else if (key === 'ctrl' && !event.ctrlKey) match = false;
            else if (key === 'shift' && !event.shiftKey) match = false;
            else if (key === 'enter' && event.key !== 'Enter') match = false;
            else if (key.length === 1 && event.key.toLowerCase() !== key) match = false;
            else if (key.length > 1 && !['alt', 'ctrl', 'shift', 'enter'].includes(key)) {
                // Handle special keys
                if (key === 'space' && event.key !== ' ') match = false;
                else if (event.key.toLowerCase() !== key) match = false;
            }
        });

        return match;
    }

    function handleQuickFix(element) {
        if (!settings.enabled || isProcessing) return;

        const text = getElementText(element);
        if (text.length < settings.correction.minTextLength) return;

        fixWithLanguageTool(text, settings.language.main)
            .then(fixedText => {
                setElementText(element, fixedText);
                showNotification(element, 'Quick fix applied', 'success');
            })
            .catch(error => {
                console.error('Quick fix error:', error);
                showNotification(element, 'Quick fix failed', 'error');
            });
    }

    // UI Management
    function setupElementObservers() {
        if (!settings.enabled || !settings.ui.showIcons) return;

        // Add to existing elements
        setTimeout(() => {
            document.querySelectorAll('textarea, input[type="text"], input[type="email"], input[type="search"], [contenteditable="true"]')
                .forEach(addSmartIconsToElement);
        }, 1000);

        document.addEventListener('focusin', function(e) {
            if (isTextElement(e.target)) {
                addSmartIconsToElement(e.target);
            }
        });
    }

    function isTextElement(element) {
        return element.tagName === 'TEXTAREA' ||
               (element.tagName === 'INPUT' && (
                   element.type === 'text' ||
                   element.type === 'email' ||
                   element.type === 'search' ||
                   element.type === 'url' ||
                   !element.type
               )) ||
               element.isContentEditable;
    }

    function addSmartIconsToElement(element) {
        if (element._grammarIconsAdded || !settings.ui.showIcons) return;

        const rect = element.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) return;

        const icon = document.createElement('div');
        icon.className = 'grammar-helper-icon';
        icon.innerHTML = 'A<div class="grammar-language-badge">LT</div>';
        icon.title = `Fix Grammar (${settings.shortcuts.smartFix})`;

        icon.addEventListener('click', async () => {
            const text = getElementText(element);
            if (text.trim()) {
                await handleSmartGrammarFix(element);
            }
        });

        positionIcon(icon, element);
        document.body.appendChild(icon);

        element._grammarIconsAdded = true;
    }

    function positionIcon(icon, element) {
        const rect = element.getBoundingClientRect();
        const scrollX = window.scrollX || window.pageXOffset;
        const scrollY = window.scrollY || window.pageYOffset;

        const top = rect.top + scrollY - 30;
        const left = rect.right + scrollX - 30;

        icon.style.top = top + 'px';
        icon.style.left = left + 'px';
    }

    function removeAllUIElements() {
        document.querySelectorAll('.grammar-helper-icon, .grammar-notification')
            .forEach(el => el.remove());

        document.querySelectorAll('[class*="grammar"]').forEach(el => {
            if (el._grammarIconsAdded) {
                el._grammarIconsAdded = false;
            }
        });
    }

    // Core Grammar Functions
    async function handleSmartGrammarFix(element) {
        if (isProcessing) {
            showNotification(element, 'Already fixing grammar...', 'warning');
            return;
        }

        isProcessing = true;

        const text = getElementText(element);
        if (!text.trim() || text.length < settings.correction.minTextLength) {
            showNotification(element, 'Text too short to fix', 'warning');
            isProcessing = false;
            return;
        }

        if (text.length > settings.correction.maxTextLength) {
            showNotification(element, 'Text too long to fix', 'warning');
            isProcessing = false;
            return;
        }

        try {
            // Add processing animation to icon
            const icon = element.parentNode?.querySelector('.grammar-helper-icon');
            if (icon && settings.ui.animations) {
                icon.classList.add('processing');
            }

            showNotification(element, 'Fixing grammar...', 'processing');

            let languageCode = settings.language.correctionLanguage;

            // Auto-detect language if set to auto
            if (languageCode === 'auto') {
                if (settings.language.autoDetect && settings.apiKeys.detectlanguage) {
                    try {
                        const detectedLang = await detectLanguage(text);
                        languageCode = detectedLang.code;
                        console.log(`🌍 Detected language: ${detectedLang.name} (${detectedLang.code})`);
                    } catch (error) {
                        console.warn('Language detection failed, using main language:', error);
                        languageCode = settings.language.main;
                    }
                } else {
                    languageCode = settings.language.main;
                }
            }

            const fixedText = await fixWithLanguageTool(text, languageCode);
            setElementText(element, fixedText);

            // Remove processing animation
            if (icon && settings.ui.animations) {
                icon.classList.remove('processing');
            }

            showNotification(element, `Grammar fixed! (${LANGUAGE_SUPPORT[languageCode] || languageCode})`, 'success');

        } catch (error) {
            console.error('Smart grammar fix error:', error);

            // Remove processing animation on error
            const icon = element.parentNode?.querySelector('.grammar-helper-icon');
            if (icon && settings.ui.animations) {
                icon.classList.remove('processing');
            }

            showNotification(element, 'Failed to fix grammar', 'error');
        } finally {
            isProcessing = false;
        }
    }

    async function detectLanguage(text) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: API_CONFIG.detectlanguage,
                headers: {
                    'Authorization': `Bearer ${settings.apiKeys.detectlanguage}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({ q: text }),
                timeout: 10000,
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.data && data.data.detections && data.data.detections.length > 0) {
                            const detection = data.data.detections[0];
                            if (detection.confidence >= settings.language.confidenceThreshold) {
                                const langName = LANGUAGE_SUPPORT[detection.language] || detection.language;
                                resolve({
                                    code: detection.language,
                                    name: langName,
                                    confidence: detection.confidence
                                });
                            }
                        }
                        reject('No confident language detection');
                    } catch (e) {
                        reject('Error parsing language detection response: ' + e);
                    }
                },
                onerror: reject,
                ontimeout: () => reject('Language detection timeout')
            });
        });
    }

    async function fixWithLanguageTool(text, languageCode) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: API_CONFIG.languagetool,
                data: `text=${encodeURIComponent(text)}&language=${languageCode}&enabledOnly=false`,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                timeout: 15000,
                onload: function(response) {
                    try {
                        if (response.status !== 200) {
                            reject(`LanguageTool API error: ${response.status}`);
                            return;
                        }

                        const result = JSON.parse(response.responseText);
                        let fixedText = text;

                        if (result.matches && result.matches.length > 0) {
                            const matches = result.matches.sort((a, b) => b.offset - a.offset);

                            for (const match of matches) {
                                if (match.replacements && match.replacements.length > 0) {
                                    const replacement = match.replacements[0].value;
                                    const before = fixedText.substring(0, match.offset);
                                    const after = fixedText.substring(match.offset + match.length);
                                    fixedText = before + replacement + after;
                                }
                            }
                        }

                        resolve(fixedText);
                    } catch (e) {
                        reject('Error parsing LanguageTool response: ' + e);
                    }
                },
                onerror: reject,
                ontimeout: () => reject('LanguageTool request timeout')
            });
        });
    }

    async function handleFixAndSend(element) {
        await handleSmartGrammarFix(element);
        if (settings.correction.autoFixOnSend) {
            setTimeout(() => clickSendButton(element), 500);
        }
    }

    function clickSendButton(element) {
        const sendSelectors = [
            'button[type="submit"]', 'input[type="submit"]',
            'button[data-testid*="send"]', 'button[data-testid*="submit"]',
            '[aria-label*="send" i]', '[aria-label*="submit" i]'
        ];

        let sendButton = null;
        const form = element.closest('form');

        if (form) {
            for (const selector of sendSelectors) {
                sendButton = form.querySelector(selector);
                if (sendButton && sendButton.offsetParent !== null) break;
            }
        }

        if (sendButton) {
            sendButton.click();
            return true;
        }

        return false;
    }

    // Utility Functions
    function getElementText(element) {
        if (element.tagName === 'TEXTAREA' || element.tagName === 'INPUT') {
            return element.value;
        } else {
            return element.textContent || element.innerText || '';
        }
    }

    function setElementText(element, text) {
        if (element.tagName === 'TEXTAREA' || element.tagName === 'INPUT') {
            element.value = text;
        } else {
            element.textContent = text;
        }
        element.dispatchEvent(new Event('input', { bubbles: true }));
        element.dispatchEvent(new Event('change', { bubbles: true }));
    }

    function showNotification(element, message, type = 'info') {
        if (!settings.ui.showNotifications) return;

        // Remove existing notification
        const existingNotification = document.querySelector('.grammar-notification');
        if (existingNotification) {
            if (settings.ui.animations) {
                existingNotification.classList.add('hide');
                setTimeout(() => existingNotification.remove(), 300);
            } else {
                existingNotification.remove();
            }
        }

        const notification = document.createElement('div');
        notification.className = `grammar-notification ${type}`;
        notification.textContent = message;

        const rect = element.getBoundingClientRect();
        const scrollX = window.scrollX || window.pageXOffset;
        const scrollY = window.scrollY || window.pageYOffset;

        notification.style.top = (rect.top + scrollY - 40) + 'px';
        notification.style.left = (rect.right + scrollX - 10) + 'px';

        document.body.appendChild(notification);

        // Add show animation
        if (settings.ui.animations) {
            setTimeout(() => {
                notification.classList.add('show');
            }, 10);

            // Auto hide after 3 seconds
            setTimeout(() => {
                notification.classList.add('hide');
                setTimeout(() => {
                    if (notification.parentNode) notification.remove();
                }, 300);
            }, 3000);
        } else {
            // No animation - just remove after 3 seconds
            setTimeout(() => {
                if (notification.parentNode) notification.remove();
            }, 3000);
        }
    }

    function showGlobalNotification(message, type = 'info', duration = 3000) {
        if (!settings.ui.showNotifications) return;

        const notification = document.createElement('div');
        notification.className = `grammar-global-notification ${type}`;
        notification.textContent = message;

        document.body.appendChild(notification);

        if (settings.ui.animations) {
            setTimeout(() => {
                notification.classList.add('show');
            }, 10);

            setTimeout(() => {
                notification.classList.remove('show');
                setTimeout(() => {
                    if (notification.parentNode) notification.remove();
                }, 300);
            }, duration);
        } else {
            setTimeout(() => {
                if (notification.parentNode) notification.remove();
            }, duration);
        }
    }

    // Settings Panel
    function showSettingsPanel() {
        const existingPanel = document.getElementById('grammar-settings-panel');
        if (existingPanel) existingPanel.remove();

        const existingOverlay = document.getElementById('grammar-settings-overlay');
        if (existingOverlay) existingOverlay.remove();

        const overlay = document.createElement('div');
        overlay.id = 'grammar-settings-overlay';
        overlay.className = 'grammar-settings-overlay';

        const panel = document.createElement('div');
        panel.id = 'grammar-settings-panel';
        panel.className = 'grammar-settings-panel';

        panel.innerHTML = `
        <div class="grammar-settings-header">
            <div class="grammar-settings-title">
                <h2>⚙️ Grammar Fixer Settings</h2>
                <div class="grammar-settings-subtitle">LanguageTool + DetectLanguage</div>
            </div>
            <button class="grammar-settings-close" id="settings-close">×</button>
        </div>

        <div class="grammar-settings-content">
            <div class="settings-section">
                <h3>Core Settings</h3>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="enabled" ${settings.enabled ? 'checked' : ''}>
                        Enable Grammar Fixer
                    </label>
                </div>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="showIcons" ${settings.ui.showIcons ? 'checked' : ''}>
                        Show Helper Icons
                    </label>
                </div>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="showNotifications" ${settings.ui.showNotifications ? 'checked' : ''}>
                        Show Notifications
                    </label>
                </div>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="animations" ${settings.ui.animations ? 'checked' : ''}>
                        Enable Animations
                    </label>
                </div>
            </div>

            <div class="settings-section">
                <h3>Language Settings</h3>
                <div class="settings-row">
                    <label>Main Language:</label>
                    <select id="mainLanguage">
                        ${Object.entries(LANGUAGE_SUPPORT).map(([code, name]) =>
                            `<option value="${code}" ${settings.language.main === code ? 'selected' : ''}>
                                ${name}
                            </option>`
                        ).join('')}
                    </select>
                </div>
                <div class="settings-row">
                    <label>Correction Language:</label>
                    <select id="correctionLanguage">
                        <option value="auto" ${settings.language.correctionLanguage === 'auto' ? 'selected' : ''}>Auto-detect</option>
                        ${Object.entries(LANGUAGE_SUPPORT).map(([code, name]) =>
                            `<option value="${code}" ${settings.language.correctionLanguage === code ? 'selected' : ''}>
                                ${name}
                            </option>`
                        ).join('')}
                    </select>
                </div>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="autoDetect" ${settings.language.autoDetect ? 'checked' : ''}>
                        Auto-detect Language (for auto mode)
                    </label>
                </div>
            </div>

            <div class="settings-section">
                <h3>Keyboard Shortcuts</h3>
                ${Object.entries(settings.shortcuts).map(([action, shortcut]) => `
                <div class="settings-row">
                    <label>${formatActionName(action)}:</label>
                    <input type="text" class="shortcut-input" id="shortcut-${action}" value="${shortcut}" readonly>
                    <button class="grammar-settings-btn secondary change-shortcut" data-action="${action}">Change</button>
                </div>
                `).join('')}
            </div>

            <div class="settings-section">
                <h3>API Configuration</h3>
                <div class="settings-row">
                    <label>DetectLanguage API Key:</label>
                    <input type="password" id="detectlanguageKey" value="${settings.apiKeys.detectlanguage}" placeholder="Enter your API key">
                </div>
                <small>Get free API key from detectlanguage.com</small>
            </div>

            <div class="settings-section">
                <h3>Text Processing</h3>
                <div class="settings-row">
                    <label>Min Text Length:</label>
                    <input type="number" id="minTextLength" value="${settings.correction.minTextLength}" min="1" max="100">
                </div>
                <div class="settings-row">
                    <label>
                        <input type="checkbox" id="autoFixOnSend" ${settings.correction.autoFixOnSend ? 'checked' : ''}>
                        Auto-fix on Send
                    </label>
                </div>
            </div>
        </div>

        <div class="grammar-settings-footer">
            <button class="grammar-settings-btn secondary" id="settings-cancel">Cancel</button>
            <button class="grammar-settings-btn primary" id="settings-save">Save Settings</button>
        </div>
        `;

        // Event listeners
        panel.querySelector('#settings-close').addEventListener('click', closeSettings);
        panel.querySelector('#settings-cancel').addEventListener('click', closeSettings);
        panel.querySelector('#settings-save').addEventListener('click', saveSettingsFromPanel);

        // Shortcut recording
        panel.querySelectorAll('.change-shortcut').forEach(button => {
            button.addEventListener('click', function() {
                const action = this.getAttribute('data-action');
                const input = panel.querySelector(`#shortcut-${action}`);
                startShortcutRecording(input, action);
            });
        });

        overlay.addEventListener('click', closeSettings);

        function closeSettings() {
            if (settings.ui.animations) {
                panel.style.animation = 'scaleIn 0.3s ease reverse';
                overlay.style.animation = 'fadeIn 0.3s ease reverse';
                setTimeout(() => {
                    panel.remove();
                    overlay.remove();
                }, 300);
            } else {
                panel.remove();
                overlay.remove();
            }
        }

        function startShortcutRecording(input, action) {
            input.value = 'Press new shortcut...';
            input.classList.add('recording');

            const handleKeyDown = (e) => {
                e.preventDefault();
                e.stopPropagation();

                // Ignore modifier keys alone
                if (['Alt', 'Control', 'Shift', 'Meta'].includes(e.key)) {
                    return;
                }

                let shortcut = '';
                if (e.altKey) shortcut += 'Alt+';
                if (e.ctrlKey) shortcut += 'Ctrl+';
                if (e.shiftKey) shortcut += 'Shift+';

                if (e.key === ' ') {
                    shortcut += 'Space';
                } else if (e.key === 'Enter') {
                    shortcut += 'Enter';
                } else if (e.key.length === 1) {
                    shortcut += e.key.toUpperCase();
                } else {
                    shortcut += e.key;
                }

                input.value = shortcut;
                input.classList.remove('recording');
                document.removeEventListener('keydown', handleKeyDown);
            };

            document.addEventListener('keydown', handleKeyDown, { once: false });

            // Cancel on escape
            const handleEscape = (e) => {
                if (e.key === 'Escape') {
                    input.value = settings.shortcuts[action];
                    input.classList.remove('recording');
                    document.removeEventListener('keydown', handleKeyDown);
                    document.removeEventListener('keydown', handleEscape);
                }
            };

            document.addEventListener('keydown', handleEscape, { once: true });
        }

        async function saveSettingsFromPanel() {
            settings.enabled = panel.querySelector('#enabled').checked;
            settings.ui.showIcons = panel.querySelector('#showIcons').checked;
            settings.ui.showNotifications = panel.querySelector('#showNotifications').checked;
            settings.ui.animations = panel.querySelector('#animations').checked;
            settings.language.main = panel.querySelector('#mainLanguage').value;
            settings.language.correctionLanguage = panel.querySelector('#correctionLanguage').value;
            settings.language.autoDetect = panel.querySelector('#autoDetect').checked;
            settings.apiKeys.detectlanguage = panel.querySelector('#detectlanguageKey').value;
            settings.correction.minTextLength = parseInt(panel.querySelector('#minTextLength').value);
            settings.correction.autoFixOnSend = panel.querySelector('#autoFixOnSend').checked;

            // Save shortcuts
            Object.keys(settings.shortcuts).forEach(action => {
                const input = panel.querySelector(`#shortcut-${action}`);
                if (input && input.value !== 'Press new shortcut...') {
                    settings.shortcuts[action] = input.value;
                }
            });

            await saveSettings();
            closeSettings();
            showGlobalNotification('Settings saved successfully!', 'success');
        }

        document.body.appendChild(overlay);
        document.body.appendChild(panel);

        // Close on Escape
        document.addEventListener('keydown', function closeOnEscape(e) {
            if (e.key === 'Escape') {
                closeSettings();
                document.removeEventListener('keydown', closeOnEscape);
            }
        });
    }

    function formatActionName(action) {
        return action.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
    }

    // Initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // Global access for debugging
    unsafeWindow.grammarFixer = {
        settings: settings,
        fixText: handleSmartGrammarFix,
        showSettings: showSettingsPanel
    };

})();