Gray Swan Arena - Focus Mode

Clean UI with popup removal and focus mode for Gray Swan Arena with persistent state

// ==UserScript==
// @name         Gray Swan Arena - Focus Mode
// @namespace    http://tampermonkey.net/
// @version      2.1
// @license      MIT
// @description  Clean UI with popup removal and focus mode for Gray Swan Arena with persistent state
// @author       KarthiDreamr
// @match        https://app.grayswan.ai/*
// @grant        GM_addStyle
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// @homepage     https://github.com/yourusername/grayswan-focus-mode
// @supportURL   https://github.com/yourusername/grayswan-focus-mode/issues
// ==/UserScript==

(async function() {
    'use strict';

    // ========== CONFIGURATION ==========
    const CONFIG = {
        storageKey: 'gsa_focusMode',
        buttonPosition: { top: '14px', left: '14px' },
        animations: true,
        crossTabSync: true,
        autoApplyDelay: 100, // ms delay before applying clean mode
        popupCleanupInterval: 500, // ms interval for popup removal
        debugMode: false
    };

    // ========== UTILITY FUNCTIONS ==========
    function log(...args) {
        if (CONFIG.debugMode) {
            console.log('[GSA Focus Mode]', ...args);
        }
    }

    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // ========== LOAD PERSISTED STATE ==========
    let isFocusMode = await GM.getValue(CONFIG.storageKey, false);
    log('Initial state loaded:', isFocusMode);

    // Apply focus mode immediately if saved (prevents flicker)
    if (isFocusMode) {
        setTimeout(() => {
            document.body.classList.add('gsa-focus-mode');
            log('Focus mode applied from storage');
        }, CONFIG.autoApplyDelay);
    }

    // ========== POPUP REMOVAL SYSTEM ==========
    function removePopupComponents() {
        /* =========================================================
           1.  REMOVE UI COMPONENT 1 - Success congratulations
           ---------------------------------------------------------*/
        document.querySelectorAll('p.mb-4.text-left.text-sm').forEach(p => {
            if (p.textContent.includes('Congratulations - you have successfully submitted a working break')) {
                const container = p.closest('div.flex.w-full.flex-col');
                if (container) {
                    container.remove();
                    log('Removed success congratulations popup');
                }
            }
        });

        /* =========================================================
           2.  REMOVE UI COMPONENT 2 - Prize dialog footer
           ---------------------------------------------------------*/
        document.querySelectorAll('div[data-slot="dialog-footer"]').forEach(footer => {
            const t = footer.textContent || '';
            if (t.includes('What prizes can I win?') && t.includes('Got it')) {
                footer.remove();
                log('Removed prize dialog footer');
            }
        });

        /* =========================================================
           3.  REMOVE UI COMPONENT 3 - Break failure dialog footer
           ---------------------------------------------------------*/
        document.querySelectorAll('div[data-slot="dialog-footer"]').forEach(footer => {
            const t = footer.textContent || '';
            if (t.includes('Why did my break fail?') && t.includes('Got it')) {
                footer.remove();
                log('Removed break failure dialog footer');
            }
        });

        /* =========================================================
           4.  REMOVE UI COMPONENT 4 - Dialog headers
           ---------------------------------------------------------*/
        document.querySelectorAll('div[data-slot="dialog-header"]').forEach(header => {
            const t = header.textContent || '';
            if (t.includes('Break Successful!') || t.includes('Break Rejected')) {
                header.remove();
                log('Removed dialog header');
            }
        });
    }

    // Start popup cleanup interval
    setInterval(removePopupComponents, CONFIG.popupCleanupInterval);
    log('Popup cleanup system started');

    // ========== ENHANCED CSS STYLES ==========
    GM_addStyle(`
        /* Hide header - but preserve lightbulb and its popup */
        .gsa-focus-mode .bg-background.fixed.top-0:not([aria-labelledby]):not([data-tooltip]):not([role="tooltip"]) {
            display: none !important;
        }

        /* Hide main sidebar - but NOT the documentation sidebar */
        .gsa-focus-mode #sidebar:not([aria-labelledby="behaviorDocDrawerTitle"]) {
            display: none !important;
        }

        .gsa-focus-mode .relative.hidden.h-full.md\\:block:not([aria-labelledby]) {
            display: none !important;
        }

        /* CRITICAL: Always show the lightbulb documentation sidebar */
        .gsa-focus-mode aside[aria-labelledby="behaviorDocDrawerTitle"],
        .gsa-focus-mode aside.fixed.top-0.right-0,
        .gsa-focus-mode aside.__web-inspector-hide-shortcut__ {
            display: flex !important;
            visibility: visible !important;
            opacity: 1 !important;
            pointer-events: auto !important;
            z-index: 9999 !important;
        }

        /* EXCEPTION: Always preserve tooltip/popup/dropdown elements */
        .gsa-focus-mode [data-tooltip],
        .gsa-focus-mode [role="tooltip"],
        .gsa-focus-mode [data-popover],
        .gsa-focus-mode [data-dropdown],
        .gsa-focus-mode [data-slot="tooltip"],
        .gsa-focus-mode [data-slot="popover"],
        .gsa-focus-mode [data-slot="dropdown-menu"],
        .gsa-focus-mode .tooltip,
        .gsa-focus-mode .popover,
        .gsa-focus-mode .dropdown-menu {
            display: block !important;
            visibility: visible !important;
            opacity: 1 !important;
            pointer-events: auto !important;
        }

        /* EXCEPTION: Lightbulb button and any trigger elements */
        .gsa-focus-mode button[data-tooltip-trigger],
        .gsa-focus-mode button:has(svg.lucide-lightbulb),
        .gsa-focus-mode [id*="bits-"]:not(#gsa-focus-toggle):not(#gsa-status-indicator) {
            display: flex !important;
            visibility: visible !important;
        }

        /* Hide breadcrumb navigation */
        .gsa-focus-mode nav[data-slot="breadcrumb"] {
            display: none !important;
        }

        /* Hide top tab navigation */
        .gsa-focus-mode .scrollbar-hide.-mb-1.flex.max-w-screen {
            display: none !important;
        }

        /* Hide mobile menu button */
        .gsa-focus-mode .md\\:hidden button[data-slot="sheet-trigger"] {
            display: none !important;
        }

        /* ENHANCED: Remove all padding and margins from main containers */
        .gsa-focus-mode .flex.flex-1.flex-col.overflow-hidden.pt-\\[4\\.5rem\\] {
            padding-top: 0 !important;
            padding-left: 0 !important;
            padding-right: 0 !important;
        }

        .gsa-focus-mode .relative.mx-auto.flex.h-full.w-full.flex-1 {
            padding: 0 !important;
            margin: 0 !important;
            max-width: none !important;
        }

        .gsa-focus-mode .flex.md\\:h-full.md\\:w-full.md\\:gap-4 {
            gap: 0 !important;
            padding: 0 !important;
        }

        /* ENHANCED: Make chat area completely full screen */
        .gsa-focus-mode .relative.flex.h-full.w-full.flex-col.gap-3 {
            max-width: none !important;
            margin: 0 !important;
            padding: 8px !important;
            gap: 8px !important;
        }

        /* ENHANCED: Remove padding from chat container */
        .gsa-focus-mode .dark\\:border-border.relative.flex.min-h-0.flex-1.flex-col.overflow-hidden {
            margin: 0 !important;
        }

        /* ENHANCED: Optimize chat content area spacing */
        .gsa-focus-mode .flex.h-full.min-h-0.flex-1.flex-col.overflow-auto {
            padding: 12px !important;
        }

        /* ENHANCED: Remove excessive margins from chat messages area */
        .gsa-focus-mode .mx-auto.flex.h-full.w-full.max-w-prose.flex-col.items-center.justify-center.space-y-2 {
            max-width: none !important;
            margin: 0 !important;
            padding: 0 !important;
        }

        /* ENHANCED: Optimize input area spacing */
        .gsa-focus-mode .relative.mx-auto.w-full.max-w-\\[75ch\\].shrink-0 {
            max-width: none !important;
            margin: 0 !important;
            padding: 8px !important;
        }

        /* ENHANCED: Remove body margins in focus mode */
        .gsa-focus-mode body {
            margin: 0 !important;
            padding: 0 !important;
        }

        /* ENHANCED: Make main content container full height */
        .gsa-focus-mode .flex.min-h-screen.w-full.flex-col.overflow-hidden {
            min-height: 100vh !important;
        }

        /* ========== ENHANCED TOGGLE BUTTON STYLES ========== */
        #gsa-focus-toggle {
            position: fixed;
            top: ${CONFIG.buttonPosition.top};
            left: ${CONFIG.buttonPosition.left};
            z-index: 10000;
            background: #1a1a1a;
            color: #e5e5e5;
            border: 1px solid #333;
            border-radius: 20px;
            padding: 6px 12px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            transition: all 0.2s ease;
            backdrop-filter: blur(8px);
            opacity: 0.8;
            user-select: none;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        #gsa-focus-toggle:hover {
            background: #2a2a2a;
            border-color: #555;
            opacity: 1;
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
        }

        #gsa-focus-toggle.active {
            background: #1a1a1a;
            border-color: #30363d;
            color: #58a6ff;
        }

        #gsa-focus-toggle.active:hover {
            background: #2a2a2a;
            border-color: #58a6ff;
        }

        /* ========== STATUS INDICATOR ========== */
        #gsa-status-indicator {
            position: fixed;
            top: ${CONFIG.buttonPosition.top};
            left: calc(${CONFIG.buttonPosition.left} + 120px);
            z-index: 9999;
            background: rgba(0, 0, 0, 0.8);
            color: #fff;
            padding: 4px 8px;
            border-radius: 12px;
            font-size: 10px;
            font-family: monospace;
            opacity: 0;
            transition: opacity 0.3s ease;
            pointer-events: none;
            backdrop-filter: blur(4px);
        }

        #gsa-status-indicator.show {
            opacity: 1;
        }

        #gsa-status-indicator.success {
            background: rgba(34, 197, 94, 0.8);
        }

        #gsa-status-indicator.sync {
            background: rgba(59, 130, 246, 0.8);
        }

        /* ========== ANIMATIONS ========== */
        ${CONFIG.animations ? `
        .gsa-focus-mode {
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .gsa-focus-mode * {
            transition: all 0.2s ease;
        }
        ` : ''}

        /* ========== RESPONSIVE IMPROVEMENTS ========== */
        @media (max-width: 768px) {
            #gsa-focus-toggle {
                top: 10px;
                left: 10px;
                padding: 5px 10px;
                font-size: 11px;
            }

            #gsa-status-indicator {
                top: 10px;
                left: 100px;
            }
        }
    `);

    // ========== CORE FUNCTIONALITY ==========
    let toggleButton;
    let statusIndicator;
    let changeListener;

    // Create UI elements
    function createUI() {
        // Toggle button
        toggleButton = document.createElement('button');
        toggleButton.id = 'gsa-focus-toggle';
        updateButtonText();
        toggleButton.title = 'Toggle focus mode (F8 or Ctrl+Shift+F)';

        // Status indicator
        statusIndicator = document.createElement('div');
        statusIndicator.id = 'gsa-status-indicator';
        statusIndicator.textContent = 'Ready';

        document.body.appendChild(toggleButton);
        document.body.appendChild(statusIndicator);

        log('UI elements created');
    }

    // Update button text and state
    function updateButtonText() {
        if (!toggleButton) return;

        if (isFocusMode) {
            toggleButton.classList.add('active');
            toggleButton.innerHTML = '↩️ <span style="font-size: 10px;">Exit</span>';
        } else {
            toggleButton.classList.remove('active');
            toggleButton.innerHTML = '🎯 <span style="font-size: 10px;">Focus</span>';
        }
    }

    // Show status message
    function showStatus(message, type = 'success', duration = 2000) {
        if (!statusIndicator) {
            log('Status indicator not ready, skipping message:', message);
            return;
        }

        statusIndicator.textContent = message;
        statusIndicator.className = `show ${type}`;

        setTimeout(() => {
            if (statusIndicator) {
                statusIndicator.classList.remove('show');
                statusIndicator.classList.remove(type);
            }
        }, duration);
    }

    // Enhanced toggle function
    async function toggleFocusMode(source = 'manual') {
        const wasActive = isFocusMode;
        isFocusMode = document.body.classList.toggle('gsa-focus-mode');

        // Update UI
        updateButtonText();

        // Save state
        try {
            await GM.setValue(CONFIG.storageKey, isFocusMode);
            log(`Mode toggled to: ${isFocusMode ? 'ON' : 'OFF'} (source: ${source})`);

            if (source === 'manual') {
                showStatus(
                    isFocusMode ? 'Focus Mode ON' : 'Focus Mode OFF',
                    'success'
                );
            } else if (source === 'sync') {
                showStatus('Synced from other tab', 'sync', 1500);
            }
        } catch (error) {
            console.error('[GSA Focus Mode] Failed to save state:', error);
            showStatus('Save failed', 'error');
        }
    }

    // Debounced toggle for performance
    const debouncedToggle = debounce(toggleFocusMode, 100);

    // ========== CROSS-TAB SYNCHRONIZATION ==========
    function setupCrossTabSync() {
        if (!CONFIG.crossTabSync) return;

        changeListener = GM_addValueChangeListener(
            CONFIG.storageKey,
            (name, oldVal, newVal, remote) => {
                if (!remote) return; // Ignore changes from this tab

                log('Received remote state change:', newVal);
                isFocusMode = !!newVal;

                // Apply state change
                document.body.classList.toggle('gsa-focus-mode', isFocusMode);
                updateButtonText();

                // Show sync notification
                showStatus('Synced from other tab', 'sync', 1500);
            }
        );

        log('Cross-tab synchronization enabled');
    }

    // ========== KEYBOARD SHORTCUTS ==========
    function setupKeyboardShortcuts() {
        document.addEventListener('keydown', function(e) {
            // F8 shortcut
            if (e.key === 'F8') {
                e.preventDefault();
                debouncedToggle('keyboard');
                return;
            }

            // Ctrl+Shift+F shortcut
            if (e.ctrlKey && e.shiftKey && e.key === 'F') {
                e.preventDefault();
                debouncedToggle('keyboard');
                return;
            }

            // Escape to exit focus mode
            if (e.key === 'Escape' && isFocusMode) {
                debouncedToggle('keyboard');
                return;
            }
        });

        log('Keyboard shortcuts registered: F8, Ctrl+Shift+F, Escape');
    }

    // ========== AUTO-DETECTION AND ADAPTATION ==========
    function adaptToPageChanges() {
        // Observer for dynamic content changes
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList' && isFocusMode) {
                    // Re-apply focus mode classes if needed
                    setTimeout(() => {
                        if (!document.body.classList.contains('gsa-focus-mode')) {
                            document.body.classList.add('gsa-focus-mode');
                            log('Focus mode re-applied after DOM change');
                        }
                    }, 50);
                }
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        log('DOM observer started for dynamic content adaptation');
    }

    // ========== INITIALIZATION ==========
    function initialize() {
        createUI();
        setupCrossTabSync();
        setupKeyboardShortcuts();
        adaptToPageChanges();

        // Button click event
        toggleButton.addEventListener('click', () => debouncedToggle('manual'));

        // Apply saved state with proper timing
        if (isFocusMode) {
            updateButtonText();
            // Delay the status message to ensure UI is ready
            setTimeout(() => showStatus('Focus Mode restored', 'success', 1500), 100);
        }

        log('Script fully initialized');
        
        // Delay the loaded message to ensure statusIndicator is fully ready
        setTimeout(() => {
            showStatus('GSA Focus Mode loaded', 'success', 2000);
        }, 200);
    }


    // ========== CLEANUP ==========
    window.addEventListener('beforeunload', function() {
        if (changeListener && CONFIG.crossTabSync) {
            GM_removeValueChangeListener(changeListener);
            log('Cleanup completed');
        }
    });

    // ========== START ==========
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        // DOM already loaded
        setTimeout(initialize, 100);
    }

    // Fallback initialization
    window.addEventListener('load', function() {
        if (!toggleButton) {
            log('Fallback initialization triggered');
            initialize();
        }
    });

    log('Gray Swan Arena Focus Mode script loaded successfully');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址