GeoPixels Guild Overhaul

Make the guild modal draggable and non-obscuring, similar to ghostImageModal

当前为 2025-11-15 提交的版本,查看 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         GeoPixels Guild Overhaul
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Make the guild modal draggable and non-obscuring, similar to ghostImageModal
// @author       ariapokoteng
// @match        *://geopixels.net/*
// @match        *://*.geopixels.net/*
// @grant        none
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=geopixels.net
// ==/UserScript==

(function() {
    'use strict';

    // Add CSS styles for dragging
    const style = document.createElement('style');
    style.textContent = `
        .guild-modal-header {
            touch-action: none !important;
            -webkit-user-select: none !important;
            user-select: none !important;
        }
        
        .guild-modal-header span {
            touch-action: none !important;
            -webkit-user-select: none !important;
            user-select: none !important;
            display: block;
            flex: 1;
            padding-right: 10px;
        }
        
        .draggable-panel {
            touch-action: none !important;
        }

        /* Guild message collapsible styling */
        .guild-message-section {
            border: 1px solid #e5e7eb;
            border-radius: 0.5rem;
            overflow: hidden;
        }

        .guild-message-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0.75rem;
            background-color: #f9fafb;
            cursor: pointer;
            user-select: none;
        }

        .guild-message-header:hover {
            background-color: #f3f4f6;
        }

        .guild-message-toggle {
            display: inline-block;
            width: 20px;
            height: 20px;
            text-align: center;
            line-height: 20px;
            font-weight: bold;
            color: #6b7280;
            transition: transform 0.2s ease;
        }

        .guild-message-toggle.collapsed {
            transform: rotate(-90deg);
        }

        .guild-message-content {
            max-height: 500px;
            overflow: hidden;
            transition: max-height 0.3s ease, padding 0.3s ease;
            padding: 0.75rem;
        }

        .guild-message-content.collapsed {
            max-height: 0;
            padding: 0;
        }

        /* Responsive layout for guild info grid */
        @media (max-width: 1024px) {
            #infoTab .grid.grid-cols-1.lg\\:grid-cols-3 {
                grid-template-columns: 1fr !important;
            }

            #infoTab .lg\\:col-span-2 {
                grid-column: auto !important;
            }

            #infoTab .lg\\:col-span-1 {
                grid-column: auto !important;
                order: 1;
            }

            #infoTab > .grid {
                display: flex;
                flex-direction: column;
            }

            #guildMembersContainer {
                order: 1;
                margin-top: 2rem;
            }
        }

        /* Force members container to bottom when message is collapsed */
        #infoTab.message-collapsed > .grid {
            display: block;
        }

        #infoTab.message-collapsed #guildMembersContainer {
            margin-top: 1rem;
        }
    `;
    document.head.appendChild(style);

    // Wait for the modal to exist
    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const startTime = Date.now();
            const checkInterval = setInterval(() => {
                const element = document.querySelector(selector);
                if (element) {
                    clearInterval(checkInterval);
                    resolve(element);
                } else if (Date.now() - startTime > timeout) {
                    clearInterval(checkInterval);
                    reject(new Error(`Element ${selector} not found within ${timeout}ms`));
                }
            }, 100);
        });
    }

    // Transform the guild modal to be draggable and floating
    async function transformGuildModal() {
        try {
            await waitForElement('#myGuildModal', 10000);

            const modal = document.getElementById('myGuildModal');
            const panel = document.getElementById('myGuildPanel');

            if (!modal || !panel) {
                console.error('[Guild Modal] myGuildModal or myGuildPanel not found');
                return;
            }

            // Check if already transformed
            if (panel.classList.contains('draggable-panel')) {
                return;
            }

            // Update modal styles to remove the overlay and make it draggable
            modal.style.position = 'fixed';
            modal.style.inset = 'auto';
            modal.style.backgroundColor = 'transparent';
            modal.style.justifyContent = 'flex-start';
            modal.style.alignItems = 'flex-start';
            modal.style.padding = '0';
            modal.style.pointerEvents = 'none';

            // Update panel styles to be floating and draggable
            panel.style.position = 'fixed';
            panel.style.top = '100px';
            panel.style.left = 'calc(50% - 25rem)';
            panel.style.width = '50rem';
            panel.style.maxWidth = '90vw';
            panel.style.maxHeight = '85vh';
            panel.style.zIndex = '40';
            panel.style.cursor = 'default';
            panel.style.transform = 'none';
            panel.style.opacity = '1';
            panel.style.scale = '1';
            panel.style.pointerEvents = 'auto';
            panel.classList.add('draggable-panel');

            // Remove any existing header if present
            const existingHeader = panel.querySelector('.guild-modal-header');
            if (existingHeader) {
                existingHeader.remove();
            }

            // Create a draggable header with close button
            const headerBar = document.createElement('div');
            headerBar.className = 'guild-modal-header';
            headerBar.style.cssText = `
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                height: 40px;
                background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
                cursor: move;
                border-radius: 0.75rem 0.75rem 0 0;
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 0 16px;
                color: white;
                font-weight: 600;
                user-select: none;
                z-index: 50;
                pointer-events: auto;
            `;
            
            const titleSpan = document.createElement('span');
            titleSpan.textContent = 'Guild Panel';
            titleSpan.style.cursor = 'move';
            
            const closeBtn = document.createElement('button');
            closeBtn.textContent = '✕';
            closeBtn.style.cssText = `
                background: none;
                border: none;
                color: white;
                font-size: 24px;
                cursor: pointer;
                padding: 0;
                margin: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                width: 30px;
                height: 30px;
                border-radius: 4px;
                transition: background-color 0.2s;
            `;
            closeBtn.onmouseover = () => closeBtn.style.backgroundColor = 'rgba(255,255,255,0.2)';
            closeBtn.onmouseout = () => closeBtn.style.backgroundColor = 'transparent';
            closeBtn.onclick = (e) => {
                e.stopPropagation();
                if (window.toggleMyGuildModal) {
                    window.toggleMyGuildModal();
                }
            };
            
            headerBar.appendChild(titleSpan);
            headerBar.appendChild(closeBtn);

            // Create resize handle
            const resizeHandle = document.createElement('div');
            resizeHandle.className = 'guild-modal-resize';
            resizeHandle.style.cssText = `
                position: absolute;
                bottom: 0;
                right: 0;
                width: 20px;
                height: 20px;
                cursor: nwse-resize;
                background: linear-gradient(135deg, transparent 0%, #3b82f6 100%);
                border-radius: 0 0 0.75rem 0;
                z-index: 51;
                pointer-events: auto;
            `;

            // Adjust panel padding to account for header
            panel.style.paddingTop = '50px';

            // Insert header as first child of panel
            if (panel.firstChild) {
                panel.insertBefore(headerBar, panel.firstChild);
            } else {
                panel.appendChild(headerBar);
            }

            // Append resize handle
            panel.appendChild(resizeHandle);

            // Set up drag functionality
            setupDragHandling(panel, titleSpan);
            
            // Set up resize functionality
            setupResizeHandling(panel, resizeHandle);

            // Set up message collapsible
            setupMessageCollapsible();

            console.log('[Guild Modal] Transformed to draggable floating panel');

        } catch (error) {
            console.error('[Guild Modal] Error transforming modal:', error);
        }
    }

    // Set up drag handling for the panel
    function setupDragHandling(panel, header) {
        let isDragging = false;
        let startX = 0;
        let startY = 0;
        let offsetX = 0;
        let offsetY = 0;

        const onMouseDown = (e) => {
            // Don't drag if clicking on resize handle or close button
            if (e.target.closest('.guild-modal-resize') || e.target.closest('button')) {
                return;
            }

            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;

            const rect = panel.getBoundingClientRect();
            offsetX = rect.left;
            offsetY = rect.top;

            // Add dragging state to panel
            panel.style.userSelect = 'none';

            document.addEventListener('mousemove', onMouseMove, true);
            document.addEventListener('mouseup', onMouseUp, true);

            e.preventDefault();
            e.stopPropagation();
        };

        const onMouseMove = (e) => {
            if (!isDragging) return;

            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;

            const newX = offsetX + deltaX;
            const newY = offsetY + deltaY;

            // Constrain to viewport with some padding
            const maxX = window.innerWidth - 100;
            const maxY = window.innerHeight - 100;

            const constrainedX = Math.max(-panel.offsetWidth + 100, Math.min(newX, maxX));
            const constrainedY = Math.max(0, Math.min(newY, maxY));

            panel.style.left = constrainedX + 'px';
            panel.style.top = constrainedY + 'px';
            panel.style.transform = 'none';
            panel.style.position = 'fixed';

            e.preventDefault();
            e.stopPropagation();
        };

        const onMouseUp = (e) => {
            if (isDragging) {
                isDragging = false;
                panel.style.userSelect = 'auto';
                document.removeEventListener('mousemove', onMouseMove, true);
                document.removeEventListener('mouseup', onMouseUp, true);
                e.preventDefault();
                e.stopPropagation();
            }
        };

        header.addEventListener('mousedown', onMouseDown, false);
    }

    // Set up resize handling for the panel
    function setupResizeHandling(panel, resizeHandle) {
        let isResizing = false;
        let startX = 0;
        let startY = 0;
        let startWidth = 0;
        let startHeight = 0;

        const onMouseDown = (e) => {
            isResizing = true;
            startX = e.clientX;
            startY = e.clientY;
            startWidth = panel.offsetWidth;
            startHeight = panel.offsetHeight;

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);

            e.preventDefault();
        };

        const onMouseMove = (e) => {
            if (!isResizing) return;

            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;

            const newWidth = Math.max(400, startWidth + deltaX);
            const newHeight = Math.max(300, startHeight + deltaY);

            panel.style.width = newWidth + 'px';
            panel.style.height = newHeight + 'px';
        };

        const onMouseUp = () => {
            isResizing = false;
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
        };

        resizeHandle.addEventListener('mousedown', onMouseDown);
    }

    // Make the message box collapsible
    function setupMessageCollapsible() {
        const infoTab = document.getElementById('infoTab');
        const messageDiv = infoTab?.querySelector('div:has(> h3:first-child):has(#guildInfoMessage)');
        
        if (!messageDiv) return;

        // Wrap the message in a collapsible section
        const messageSection = document.createElement('div');
        messageSection.className = 'guild-message-section';

        const messageHeader = document.createElement('div');
        messageHeader.className = 'guild-message-header';

        const toggleSpan = document.createElement('span');
        toggleSpan.className = 'guild-message-toggle';
        toggleSpan.textContent = '▼';

        const titleSpan = document.createElement('span');
        titleSpan.textContent = 'Message';
        titleSpan.style.fontWeight = '500';

        messageHeader.appendChild(toggleSpan);
        messageHeader.appendChild(titleSpan);

        const messageContent = document.createElement('div');
        messageContent.className = 'guild-message-content';

        // Move the message content into the collapsible
        const h3 = messageDiv.querySelector('h3');
        const p = messageDiv.querySelector('#guildInfoMessage');

        if (h3 && p) {
            messageContent.appendChild(h3.cloneNode(true));
            messageContent.appendChild(p.cloneNode(true));
            
            // Replace the original message div
            messageDiv.parentNode.replaceChild(messageSection, messageDiv);
            messageSection.appendChild(messageHeader);
            messageSection.appendChild(messageContent);

            // Set up toggle functionality
            let isCollapsed = false;

            messageHeader.addEventListener('click', () => {
                isCollapsed = !isCollapsed;
                toggleSpan.classList.toggle('collapsed', isCollapsed);
                messageContent.classList.toggle('collapsed', isCollapsed);

                // Add/remove class to info tab for layout adjustment
                infoTab.classList.toggle('message-collapsed', isCollapsed);
            });
        }
    }

    // Initialize when page loads
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', transformGuildModal);
    } else {
        transformGuildModal();
    }

    console.log('[Guild Modal] v1.1 - Loaded');

})();