Gray Swan Arena - Focus Mode

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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');
})();