Greasy Fork

Greasy Fork is available in English.

Grok Feature Flags OLD DEPRICATED

Toggle feature flags on grok.com to test out new features

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Grok Feature Flags OLD DEPRICATED
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Toggle feature flags on grok.com to test out new features
// @author       Blankspeaker
// @match        https://grok.com/*
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(async function() {
    'use strict';

    try {
        // Function to find and fetch the JavaScript file containing local_feature_flags
        async function findFlagsFile() {
            const scripts = Array.from(document.getElementsByTagName('script')).map(script => script.src).filter(src => src);
            for (const src of scripts) {
                if (src.includes('.js') && !src.includes('polyfill')) {
                    try {
                        const response = await fetch(src);
                        const text = await response.text();
                        if (text.includes('"local_feature_flags"')) {
                            console.log("Found flags file:", src);
                            return { src, text };
                        }
                    } catch (error) {
                        console.warn("Failed to fetch script:", src, error);
                    }
                }
            }
            console.error("No JavaScript file found containing local_feature_flags.");
            return null;
        }

        // Function to scrape flags from the JavaScript file
        function scrapeFlags(fileContent) {
            // Match flag definitions like SHOW_FAVORITE_BUTTON: e("show_favorite_button")
            const regex = /\b([A-Z_]+)\s*:\s*e\s*\(\s*"([a-z_]+)"\s*\)/g;
            const flags = {};
            let match;
            while ((match = regex.exec(fileContent)) !== null) {
                const upperKey = match[1]; // e.g., SHOW_FAVORITE_BUTTON
                const lowerKey = match[2]; // e.g., show_favorite_button
                flags[lowerKey] = false;
                flags[upperKey] = false;
            }
            // Handle non-boolean flags (preserve if present)
            const nonBooleanRegex = /\b([A-Z_]+)\s*:\s*t\s*\(\s*"([a-z_]+)"\s*\)/g;
            while ((match = nonBooleanRegex.exec(fileContent)) !== null) {
                const upperKey = match[1];
                const lowerKey = match[2];
                // Only include if already in localStorage (to avoid UI issues)
                if (currentFlags[upperKey] !== undefined) flags[upperKey] = currentFlags[upperKey];
                if (currentFlags[lowerKey] !== undefined) flags[lowerKey] = currentFlags[lowerKey];
            }
            return flags;
        }

        // Check localStorage availability (for incognito mode)
        let localStorageAvailable = true;
        try {
            localStorage.setItem("test", "test");
            localStorage.removeItem("test");
        } catch (error) {
            localStorageAvailable = false;
            console.warn("localStorage is restricted (e.g., incognito mode):", error);
        }

        // Read current flags from localStorage
        let rawFlags = localStorageAvailable ? localStorage.getItem("local_feature_flags") : null;
        console.log("Raw local_feature_flags:", rawFlags);
        let currentFlags = rawFlags ? JSON.parse(rawFlags) : {};

        // If localStorage is empty or unavailable, scrape flags from source
        if (Object.keys(currentFlags).length === 0) {
            console.log("No flags in localStorage. Attempting to scrape from source...");
            const flagsFile = await findFlagsFile();
            if (flagsFile) {
                const scrapedFlags = scrapeFlags(flagsFile.text);
                if (Object.keys(scrapedFlags).length > 0) {
                    currentFlags = { ...scrapedFlags };
                    if (localStorageAvailable) {
                        localStorage.setItem("local_feature_flags", JSON.stringify(currentFlags));
                        console.log("Scraped and initialized flags:", Object.keys(currentFlags));
                    } else {
                        console.warn("Using scraped flags in memory due to localStorage restrictions.");
                    }
                } else {
                    console.error("No flags scraped from source.");
                }
            } else {
                console.error("Unable to initialize flags: No source file found.");
            }
        }

        // Group flags by uppercase name to avoid duplicate display
        let flagGroups = {};
        Object.keys(currentFlags).forEach(flag => {
            const normalized = flag.toUpperCase();
            if (!flagGroups[normalized]) {
                flagGroups[normalized] = [];
            }
            flagGroups[normalized].push(flag);
        });
        let displayFlags = Object.keys(flagGroups);

        // Log flag counts
        console.log("Total flags:", Object.keys(currentFlags).length, "Unique flags (after normalization):", displayFlags.length);

        // Create UI element for flag picker
        let ui = document.createElement("div");
        ui.id = "feature-flags-ui";
        ui.style.display = "none"; // Initially hidden
        ui.innerHTML = `
            <div class="title-bar">
                <span>Feature Flags</span>
                <button id="minimize-btn">_</button>
                <button id="close-btn">X</button>
            </div>
            <div class="content">
                <div class="flag-list">
                    ${displayFlags.length > 0 ? displayFlags.map(normalizedFlag => {
                        // Use the first original key for checkbox state
                        const originalKeys = flagGroups[normalizedFlag];
                        const primaryKey = originalKeys[0];
                        return `
                            <label>
                                <input type="checkbox" data-flag="${normalizedFlag}" ${typeof currentFlags[primaryKey] === 'boolean' && currentFlags[primaryKey] ? 'checked' : ''}>
                                ${normalizedFlag}
                            </label>
                        `;
                    }).join("") : '<p>No feature flags available. Try opening the settings menu to populate flags.</p>'}
                </div>
                <button id="save-btn">Save</button>
            </div>
        `;

        // Append UI to body
        document.body.appendChild(ui);

        // Load saved UI state
        let uiState = localStorageAvailable ? JSON.parse(localStorage.getItem("feature_flags_ui_state") || "{}") : {};
        if (uiState.left && uiState.top) {
            ui.style.left = uiState.left + "px";
            ui.style.top = uiState.top + "px";
        }
        if (uiState.minimized) {
            ui.classList.add("minimized");
        }
        if (uiState.visible) {
            ui.style.display = "block";
        }

        // Add CSS for styling, dragging, and fixed save button
        let style = document.createElement("style");
        style.textContent = `
        #feature-flags-ui {
            position: absolute;
            top: 10px;
            left: 10px;
            width: 400px; /* Wider UI to prevent text wrapping */
            background: #333333; /* Dark gray background */
            color: #ffffff; /* White text */
            border: 1px solid #555555;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
            font-family: Arial, sans-serif;
            z-index: 10000; /* Ensure UI is above other elements */
        }

        #feature-flags-ui .title-bar {
            background: #555555; /* Slightly lighter gray for title bar */
            color: #ffffff;
            padding: 5px;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        #feature-flags-ui .title-bar span {
            flex-grow: 1;
        }

        #feature-flags-ui .content {
            padding: 10px;
            display: flex;
            flex-direction: column;
            max-height: 400px;
        }

        #feature-flags-ui .flag-list {
            flex: 1;
            overflow-y: auto;
            padding-bottom: 10px;
        }

        #feature-flags-ui.minimized .content {
            display: none;
        }

        #feature-flags-ui label {
            display: flex; /* Keep checkbox and text inline */
            align-items: center;
            margin-bottom: 5px;
            color: #ffffff; /* White text for labels */
        }

        #feature-flags-ui label input {
            margin-right: 8px; /* Space between checkbox and text */
        }

        #feature-flags-ui button {
            color: #ffffff;
            background: #555555;
            border: 1px solid #777777;
        }

        #feature-flags-ui #save-btn {
            position: sticky;
            bottom: 0;
            margin-top: 10px;
            padding: 5px;
            width: 100%;
            box-sizing: border-box;
        }

        #feature-flags-ui p {
            color: #ffffff;
            margin: 0;
        }
        `;
        document.head.appendChild(style);

        // Add menu item for Feature Flags
        const manageSubscriptionItem = document.querySelector('div[role="menuitem"] svg path[d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"]');
        if (manageSubscriptionItem) {
            const menuItem = document.createElement("div");
            menuItem.setAttribute("role", "menuitem");
            menuItem.className = "relative flex select-none items-center cursor-pointer px-3 py-2 rounded-xl text-sm outline-none focus:bg-button-ghost-hover";
            menuItem.setAttribute("tabindex", "-1");
            menuItem.setAttribute("data-orientation", "vertical");
            menuItem.setAttribute("data-radix-collection-item", "");
            menuItem.innerHTML = `
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="stroke-[2] text-neutral-400 mr-2" stroke-width="2">
                    <path d="M10 4V4C8.13623 4 7.20435 4 6.46927 4.30448C5.48915 4.71046 4.71046 5.48915 4.30448 6.46927C4 7.20435 4 8.13623 4 10V13.6C4 15.8402 4 16.9603 4.43597 17.816C4.81947 18.5686 5.43139 19.1805 6.18404 19.564C7.03968 20 8.15979 20 10.4 20H14C15.8638 20 16.7956 20 17.5307 19.6955C18.5108 19.2895 19.2895 18.5108 19.6955 17.5307C20 16.7956 20 15.8638 20 14V14" stroke="currentColor" stroke-linecap="square"></path>
                    <path d="M12.4393 14.5607L19.5 7.5C20.3284 6.67157 20.3284 5.32843 19.5 4.5C18.6716 3.67157 17.3284 3.67157 16.5 4.5L9.43934 11.5607C9.15804 11.842 9 12.2235 9 12.6213V15H11.3787C11.7765 15 12.158 14.842 12.4393 14.5607Z" stroke="currentColor" stroke-linecap="square"></path>
                </svg>
                Feature Flags
            `;
            manageSubscriptionItem.parentElement.insertAdjacentElement("afterend", menuItem);

            // Toggle flag picker visibility on menu item click
            menuItem.addEventListener("click", () => {
                ui.style.display = ui.style.display === "none" ? "block" : "none";
                uiState.visible = ui.style.display === "block";
                if (localStorageAvailable) {
                    localStorage.setItem("feature_flags_ui_state", JSON.stringify(uiState));
                }
            });
        } else {
            console.warn("Manage Subscription menu item not found. Flag picker will be shown by default.");
            ui.style.display = "block"; // Fallback to visible if menu item not found
        }

        // Make UI draggable and save position
        let titleBar = ui.querySelector(".title-bar");
        let isDragging = false;
        let offsetX, offsetY;

        titleBar.addEventListener("mousedown", (e) => {
            isDragging = true;
            offsetX = e.clientX - ui.offsetLeft;
            offsetY = e.clientY - ui.offsetTop;
        });

        document.addEventListener("mousemove", (e) => {
            if (isDragging) {
                ui.style.left = (e.clientX - offsetX) + "px";
                ui.style.top = (e.clientY - offsetY) + "px";
                // Update saved position
                uiState.left = e.clientX - offsetX;
                uiState.top = e.clientY - offsetY;
                if (localStorageAvailable) {
                    localStorage.setItem("feature_flags_ui_state", JSON.stringify(uiState));
                }
            }
        });

        document.addEventListener("mouseup", () => {
            isDragging = false;
        });

        // Minimize button and save state
        ui.querySelector("#minimize-btn").addEventListener("click", () => {
            ui.classList.toggle("minimized");
            uiState.minimized = ui.classList.contains("minimized");
            if (localStorageAvailable) {
                localStorage.setItem("feature_flags_ui_state", JSON.stringify(uiState));
            }
        });

        // Close button
        ui.querySelector("#close-btn").addEventListener("click", () => {
            ui.style.display = "none";
            uiState.visible = false;
            if (localStorageAvailable) {
                localStorage.setItem("feature_flags_ui_state", JSON.stringify(uiState));
            }
        });

        // Save button with auto-refresh
        ui.querySelector("#save-btn").addEventListener("click", () => {
            let modifiedFlags = { ...currentFlags };
            displayFlags.forEach(normalizedFlag => {
                let checkbox = ui.querySelector(`input[data-flag="${normalizedFlag}"]`);
                if (checkbox) {
                    // Update all original keys for this normalized flag
                    flagGroups[normalizedFlag].forEach(originalKey => {
                        modifiedFlags[originalKey] = checkbox.checked;
                    });
                }
            });
            if (localStorageAvailable) {
                localStorage.setItem("local_feature_flags", JSON.stringify(modifiedFlags));
            }
            console.log("Flags saved. Reloading page...");
            currentFlags = modifiedFlags; // Update in-memory flags for incognito
            location.reload(); // Auto-refresh the page
        });

        console.log("Grok Feature Flags Toggler loaded successfully with", displayFlags.length, "unique flags.");
    } catch (error) {
        console.error("Error in Grok Feature Flags Toggler:", error);
    }
})();