Model Selector for AI Uncensored (v2.7 - Strict Ultra Mode)

Select AI model, recalculates auth headers. Auto-syncs with site's Fast/Ultra mode. Ultra mode only shows reasoning models. UI enhancements + status indicator. Uses unsafeWindow.

// ==UserScript==
// @name         Model Selector for AI Uncensored (v2.7 - Strict Ultra Mode)
// @namespace    http://tampermonkey.net/
// @version      2.7
// @description  Select AI model, recalculates auth headers. Auto-syncs with site's Fast/Ultra mode. Ultra mode only shows reasoning models. UI enhancements + status indicator. Uses unsafeWindow.
// @author       saros
// @match        https://www.aiuncensored.info/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    console.log('[Model Selector] Script starting (v2.7 - Strict Ultra Mode)...');

    // --- Configuration ---
    // Define models with their types
    const AVAILABLE_MODELS_DATA = [
        { id: "deepseek-ai/DeepSeek-V3-0324", name: "DeepSeek-V3-0324", type: "normal" },
        { id: "hermes3-405b", name: "Hermes3-405b", type: "normal" },
        { id: "hermes3-8b", name: "Hermes3-8b", type: "normal" },
        { id: "hermes3-70b", name: "Hermes3-70b", type: "normal" },
        { id: "deepseek-ai/DeepSeek-R1-0528", name: "DeepSeek-R1-0528", type: "reasoning" },
        { id: "deepseek-r1-671b", name: "DeepSeek-R1-671b", type: "reasoning" },
    ];

    // Pre-filter models for convenience based on internal 'type'
    const NORMAL_MODELS = AVAILABLE_MODELS_DATA.filter(m => m.type === 'normal');
    const REASONING_MODELS = AVAILABLE_MODELS_DATA.filter(m => m.type === 'reasoning');

    const STORAGE_KEY_MODEL = 'selectedChatModel_AIUncensored_v2';
    const STORAGE_KEY_DISPLAY_MODE = 'displayMode_AIUncensored_v2'; // 'normal' or 'ultra'

    const API_ENDPOINT_PATH = '/api/chat';

    // --- Global Variables (will be set by UI creation) ---
    let selectedModel = GM_getValue(STORAGE_KEY_MODEL, NORMAL_MODELS[0]?.id || AVAILABLE_MODELS_DATA[0]?.id);
    let currentDisplayMode = GM_getValue(STORAGE_KEY_DISPLAY_MODE, 'normal'); // Default to 'normal'

    let selectDropdown; // Reference to the model selection dropdown
    let normalRadioBtn; // Reference to the 'Normal' radio button
    let ultraRadioBtn;  // Reference to the 'Ultra' radio button


    // --- Functions to update UI elements (declared early for accessibility) ---

    // Function to ensure selected model is compatible with the current display mode
    function ensureModelCompatibility() {
        let compatibleModels;
        if (currentDisplayMode === 'normal') {
            compatibleModels = NORMAL_MODELS;
        } else { // 'ultra' mode
            compatibleModels = REASONING_MODELS; // Ultra mode ONLY allows reasoning models
        }

        const modelExistsInCurrentMode = compatibleModels.some(m => m.id === selectedModel);

        if (!modelExistsInCurrentMode) {
            // If the previously selected model isn't available in the current mode,
            // default to the first available model for this mode.
            selectedModel = compatibleModels[0]?.id || AVAILABLE_MODELS_DATA[0]?.id || null; // Fallback to first available overall, or null
            if (selectedModel) {
                GM_setValue(STORAGE_KEY_MODEL, selectedModel);
                console.log(`[Model Selector] Adjusted selected model to '${selectedModel}' for '${currentDisplayMode}' mode compatibility.`);
            } else {
                GM_deleteValue(STORAGE_KEY_MODEL); // Clear if no models are available for this mode
                console.warn(`[Model Selector] No compatible models found for '${currentDisplayMode}' mode. Selected model set to null.`);
            }
        }
    }

    // Function to update the model dropdown options based on currentDisplayMode
    function updateModelDropdownOptions() {
        if (!selectDropdown) return;

        // Clear existing options
        selectDropdown.innerHTML = '';

        let modelsToDisplay;
        if (currentDisplayMode === 'normal') {
            modelsToDisplay = NORMAL_MODELS;
            if (modelsToDisplay.length === 0) {
                console.warn('[Model Selector] No "normal" models configured. Please check AVAILABLE_MODELS_DATA.');
            }
        } else { // 'ultra'
            // --- THIS IS THE KEY CHANGE ---
            modelsToDisplay = REASONING_MODELS; // Only show reasoning models for Ultra mode
            if (modelsToDisplay.length === 0) {
                 console.warn('[Model Selector] No "reasoning" models configured for Ultra mode. Please check AVAILABLE_MODELS_DATA.');
            }
        }

        let newSelectedModelAvailable = false;
        if (modelsToDisplay.length > 0) {
            modelsToDisplay.forEach(model => {
                const option = document.createElement('option');
                option.value = model.id;
                option.textContent = model.name;
                option.title = model.id; // Full ID as title for hover
                if (model.id === selectedModel) {
                    option.selected = true;
                    newSelectedModelAvailable = true;
                }
                selectDropdown.appendChild(option);
            });

            // If the previously selected model is no longer available in the new mode,
            // default to the first available model in the new mode.
            if (!newSelectedModelAvailable) {
                selectedModel = modelsToDisplay[0].id;
                GM_setValue(STORAGE_KEY_MODEL, selectedModel);
                selectDropdown.value = selectedModel; // Update dropdown selection
                console.log(`[Model Selector] Adjusted selected model to '${selectedModel}' for new mode compatibility.`);
            }
        } else {
            selectedModel = null; // No models available for this mode
            GM_deleteValue(STORAGE_KEY_MODEL); // Clear if no models
            const noModelOption = document.createElement('option');
            noModelOption.textContent = "No models available for this mode.";
            noModelOption.value = "";
            noModelOption.disabled = true;
            noModelOption.selected = true;
            selectDropdown.appendChild(noModelOption);
            console.warn("[Model Selector] No models available for the current display mode!");
        }

        // Always update status after dropdown is populated/adjusted
        updateStatusDisplay('idle', selectedModel);
    }

    // Helper function to update our custom radio buttons
    function updateRadioButtons() {
        if (normalRadioBtn && ultraRadioBtn) {
            normalRadioBtn.checked = (currentDisplayMode === 'normal');
            ultraRadioBtn.checked = (currentDisplayMode === 'ultra');
        }
    }

    // --- Replicated Header Generation Function (based on site's tF) ---
    const generateAuthHeaders = async (requestBodyObject) => {
        const timestamp = Math.floor(Date.now() / 1e3).toString();
        const bodyString = JSON.stringify(requestBodyObject);
        const payload = `${timestamp}${bodyString}`;
        const encoder = new TextEncoder();
        const secretKey = encoder.encode("your-super-secret-key-replace-in-production"); // This exact key needs to match the site's.
        const dataToSign = encoder.encode(payload);
        try {
            const importedKey = await crypto.subtle.importKey("raw", secretKey, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
            const signatureBuffer = await crypto.subtle.sign("HMAC", importedKey, dataToSign);
            const signatureHex = Array.from(new Uint8Array(signatureBuffer)).map(b => b.toString(16).padStart(2, "0")).join("");
            return {
                "X-API-Key": "62852b00cb9e44bca86f0ec7e7455dc6", "X-Timestamp": timestamp, "X-Signature": signatureHex,
                "Content-Type": "application/json", "accept": "*/*", "accept-language": "en-US,en;q=0.9",
            };
        } catch (error) { console.error("[Model Selector] Error generating signature:", error); throw error; }
    };

    // --- Function to Update Status Display ---
    function updateStatusDisplay(type, messageContent = '') {
        const indicator = document.getElementById('modelStatusIndicator');
        if (!indicator) return;

        let messagePrefix = '';
        indicator.className = 'status-indicator-base'; // Base class for styling

        let modelDisplayName = messageContent;
        // Find the friendly name for the model ID
        const modelObj = AVAILABLE_MODELS_DATA.find(m => m.id === messageContent);
        if (modelObj) {
            modelDisplayName = modelObj.name;
        } else if (messageContent === null || messageContent === '') {
            modelDisplayName = 'No model selected'; // Case for no compatible models
        }


        const modeText = currentDisplayMode === 'ultra' ? 'Ultra Mode' : 'Normal Mode';

        switch (type) {
            case 'success':
                messagePrefix = 'Using: ';
                indicator.classList.add('status-success');
                indicator.textContent = `${messagePrefix}${modelDisplayName} (${modeText})`;
                break;
            case 'no-change':
                messagePrefix = 'Already: ';
                indicator.classList.add('status-no-change');
                indicator.textContent = `${messagePrefix}${modelDisplayName} (${modeText})`;
                break;
            case 'warning':
                messagePrefix = 'Warning: ';
                indicator.classList.add('status-warning');
                indicator.textContent = `${messagePrefix}${messageContent || 'Check console.'}`;
                break;
            case 'error':
                messagePrefix = 'Error: ';
                indicator.classList.add('status-error');
                indicator.textContent = `${messagePrefix}${messageContent || 'Override failed. Check console.'}`;
                break;
            case 'selected':
                messagePrefix = 'Selected: ';
                indicator.classList.add('status-selected');
                indicator.textContent = `${messagePrefix}${modelDisplayName} (${modeText}). Awaiting API call.`;
                break;
            case 'idle':
            default:
                indicator.classList.add('status-idle');
                indicator.textContent = `Panel active. Current: ${modelDisplayName} (${modeText}). Status updates after API usage.`;
                break;
        }
    }

    // --- Intercept Fetch using unsafeWindow ---
    const originalFetch = unsafeWindow.fetch;
    unsafeWindow.fetch = async function(input, init) {
        const url = (input instanceof Request) ? input.url : input;
        const method = ((init && init.method) || (input instanceof Request && input.method) || 'GET').toUpperCase();

        if (url.endsWith(API_ENDPOINT_PATH) && method === 'POST' && init && init.body) {
            try {
                let bodyData = JSON.parse(init.body);
                // Ensure the body has a 'model' property before attempting to modify
                if (bodyData && typeof bodyData === 'object' && bodyData.hasOwnProperty('model')) {
                    // Only modify if the current model in the request doesn't match our selection
                    if (bodyData.model !== selectedModel) {
                        console.log(`[Model Selector] Intercepted ${API_ENDPOINT_PATH}. Original model: ${bodyData.model}`);
                        if (selectedModel) { // Only attempt to override if a model is actually selected
                            bodyData.model = selectedModel; // Apply the user's selected model
                            console.log(`[Model Selector] Modified model to: ${selectedModel}`);
                            console.log('[Model Selector] Regenerating auth headers...');
                            const newHeaders = await generateAuthHeaders(bodyData); // Regenerate headers with the new body
                            updateStatusDisplay('success', selectedModel); // Update status: SUCCESS
                            const newInit = { ...init, headers: newHeaders, body: JSON.stringify(bodyData) };
                            console.log('[Model Selector] Sending fetch with new headers and modified body.');
                            return originalFetch(input, newInit);
                        } else {
                            console.warn('[Model Selector] No model selected in userscript dropdown. Allowing original request with site\'s chosen model.');
                            updateStatusDisplay('warning', 'No model selected in panel. Original request sent.');
                        }
                    } else {
                        console.log(`[Model Selector] Intercepted ${API_ENDPOINT_PATH}. Model already matches selection (${selectedModel}). Passing through.`);
                        updateStatusDisplay('no-change', selectedModel); // Update status: NO CHANGE
                    }
                } else {
                     console.warn('[Model Selector] Fetch body did not contain expected "model" property or was malformed:', bodyData);
                     updateStatusDisplay('warning', 'Payload issue'); // Update status: WARNING
                }
            } catch (e) {
                console.error('[Model Selector] Error processing fetch interceptor:', e);
                updateStatusDisplay('error', `Processing error: ${e.message}`); // Update status: ERROR
            }
        }
        return originalFetch(input, init); // For all other requests, pass them through unchanged
    };

    // --- Observer for site's Fast/Ultra buttons ---
    function setupModeObserver() {
        // The container for Fast/Ultra/Call buttons.
        // Based on the provided JS snippet, this is `tF` styled component, which is a `div`
        // positioned fixed at the bottom. This selector is robust to class name changes from build.
        const chatModeContainerSelector = 'div[style*="bottom: 120px"][style*="left: 50%"][style*="fixed"]';

        const observerTarget = document.querySelector(chatModeContainerSelector);

        if (!observerTarget) {
            // Element not found immediately, retry after a short delay.
            // This is crucial because the script runs at document-start, but elements
            // might be rendered later by the SPA framework.
            console.log('[Model Selector] Chat mode container not found, retrying setupModeObserver in 500ms...');
            setTimeout(setupModeObserver, 500);
            return;
        }
        console.log('[Model Selector] Chat mode container found, setting up MutationObserver.');

        const buttons = observerTarget.querySelectorAll('button');
        let initialModeDetected = false;

        // --- Initial check for active mode when script loads ---
        buttons.forEach(button => {
            const buttonText = button.textContent.trim();
            const computedStyle = window.getComputedStyle(button);
            // Check for the specific active background color (#c15a17 in RGB)
            if (computedStyle.backgroundColor === 'rgb(193, 90, 23)') { // This is the color for active buttons
                if (buttonText === 'Fast') {
                    currentDisplayMode = 'normal';
                    initialModeDetected = true;
                } else if (buttonText === 'Ultra') { // Site's "beta" corresponds to our "ultra"
                    currentDisplayMode = 'ultra';
                    initialModeDetected = true;
                }
            }
        });

        if (initialModeDetected) {
            GM_setValue(STORAGE_KEY_DISPLAY_MODE, currentDisplayMode);
            console.log(`[Model Selector] Initial site mode detected: ${currentDisplayMode}`);
        } else {
             console.warn('[Model Selector] No active site mode button found on initial load. Defaulting to stored mode.');
        }

        // Run compatibility check and update UI based on (potentially new) currentDisplayMode
        ensureModelCompatibility(); // Ensure selectedModel is valid for this mode
        updateRadioButtons();       // Update our custom radio buttons
        updateModelDropdownOptions(); // Populate model dropdown based on the mode
        updateStatusDisplay('idle', selectedModel); // Initial status message

        // --- MutationObserver to watch for changes ---
        const observer = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                // We are only interested in attribute changes on the buttons themselves
                if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
                    const targetButton = mutation.target; // The button whose class attribute changed
                    const buttonText = targetButton.textContent.trim();
                    const computedStyle = window.getComputedStyle(targetButton);

                    // Check if the button currently has the active background color
                    const isActive = computedStyle.backgroundColor === 'rgb(193, 90, 23)';

                    let newMode = currentDisplayMode; // Assume no change

                    if (buttonText === 'Fast' && isActive) {
                        newMode = 'normal';
                    } else if (buttonText === 'Ultra' && isActive) { // Site's "beta" corresponds to our "ultra"
                        newMode = 'ultra';
                    }

                    // If a different mode is now active, update our internal state and UI
                    if (newMode !== currentDisplayMode) {
                        currentDisplayMode = newMode;
                        GM_setValue(STORAGE_KEY_DISPLAY_MODE, currentDisplayMode);
                        console.log(`[Model Selector] Site mode changed by user interaction to: ${currentDisplayMode}`);
                        // Update our custom UI elements to reflect the new mode
                        ensureModelCompatibility(); // Re-ensure model compatibility after mode change
                        updateRadioButtons();
                        updateModelDropdownOptions();
                        updateStatusDisplay('selected', selectedModel); // Indicate new selection
                    }
                }
            }
        });

        // Observe each mode button for changes to its class attribute.
        // This is important because the active state changes by adding/removing classes.
        buttons.forEach(button => {
            observer.observe(button, { attributes: true, attributeFilter: ['class'] });
        });
    }


    // --- Create Enhanced UI ---
    function createEnhancedUI() {
        console.log('[Model Selector] createEnhancedUI called');

        GM_addStyle(`
            #modelSelectorToggleBtn {
                position: fixed; top: 16px; left: 60px; z-index: 9998; background-color: #2a2a2a;
                color: #e0e0e0; border: 1px solid #c15a17; border-radius: 50%; width: 40px; height: 40px;
                font-size: 22px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.35);
                transition: background-color 0.2s, transform 0.2s, border-color 0.3s ease;
                display: flex; align-items: center; justify-content: center; line-height: 1;
            }
            #modelSelectorToggleBtn:hover { background-color: #383838; transform: translateY(-1px) scale(1.05); }

            #modelSelectorContainer {
                position: fixed; top: 70px; left: 15px; z-index: 9999; background-color: #1e1e1e;
                border: 1px solid #c15a17; padding: 0; border-radius: 10px; box-shadow: 0 5px 15px rgba(0,0,0,0.6);
                font-family: 'Segoe UI', 'Avenir', 'Arial', sans-serif; color: #e0e0e0; min-width: 290px;
                display: flex; flex-direction: column; opacity: 0; transform: translateY(-15px) scale(0.98);
                visibility: hidden; transition: opacity 0.25s ease-out, transform 0.25s ease-out, visibility 0s linear 0.25s;
            }
            #modelSelectorContainer.visible { opacity: 1; transform: translateY(0) scale(1); visibility: visible; transition-delay: 0s; }

            #modelSelectorHeader {
                background-color: #282828; padding: 10px 15px; border-bottom: 1px solid #c15a17;
                border-radius: 9px 9px 0 0; display: flex; justify-content: space-between; align-items: center;
                font-weight: 600; font-size: 15px;
            }
            #modelSelectorHeader span { color: #c15a17; font-weight: 700; }
            #modelSelectorCloseBtn {
                background: none; border: none; color: #b0b0b0; font-size: 26px; font-weight: bold;
                cursor: pointer; padding: 0 5px; line-height: 1; opacity: 0.7; transition: opacity 0.2s, color 0.2s;
            }
            #modelSelectorCloseBtn:hover { opacity: 1; color: #ffffff; }

            #modelSelectorBody { padding: 18px; display: flex; flex-direction: column; gap: 10px; }
            #modelSelectorContainer label { margin: 0 0 2px 0; font-weight: 500; color: #cccccc; font-size: 13.5px; }

            #modelSelectorDropdown {
                width: 100%; padding: 10px 12px; background-color: #2c2c2c; color: #e0e0e0;
                border: 1px solid #555; border-radius: 6px; font-size: 14px; box-sizing: border-box;
                transition: border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease;
            }
            #modelSelectorDropdown:hover { border-color: #777; }
            #modelSelectorDropdown:focus { border-color: #c15a17; box-shadow: 0 0 0 2px rgba(193, 90, 23, 0.3); outline: none; }
            #modelSelectorDropdown option { background-color: #2c2c2c; color: #e0e0e0; padding: 8px 10px; }

            /* --- Status Indicator Styles --- */
            .status-indicator-base {
                padding: 8px 10px;
                margin-top: 12px;
                border-radius: 5px;
                font-size: 12.5px;
                text-align: center;
                transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
                border: 1px solid transparent;
                line-height: 1.4;
            }
            .status-indicator-base.status-idle,
            .status-indicator-base.status-selected {
                background-color: #303030;
                color: #b0b0b0;
                border-color: #404040;
            }
            .status-indicator-base.status-success {
                background-color: #203c20; /* Darker, less saturated green */
                color: #90c090; /* Softer green text */
                border-color: #305c30;
            }
            .status-indicator-base.status-no-change {
                background-color: #22303f; /* Darker, less saturated blue */
                color: #8ab0d0; /* Softer blue text */
                border-color: #32506f;
            }
            .status-indicator-base.status-warning {
                background-color: #3f3f22; /* Darker, less saturated yellow */
                color: #c0c08a; /* Softer yellow text */
                border-color: #5f5f32;
            }
            .status-indicator-base.status-error {
                background-color: #3f2222; /* Darker, less saturated red */
                color: #d08a8a; /* Softer red text */
                border-color: #5f3232;
            }

            /* --- Display Mode Styles --- */
            #displayModeSection {
                margin-top: 10px;
                padding-top: 10px;
                border-top: 1px dashed #3a3a3a; /* Separator for display mode */
            }
            #displayModeSection label {
                margin-bottom: 5px;
            }
            #displayModeOptions {
                display: flex;
                gap: 15px; /* Space between radio buttons */
                justify-content: center;
                margin-top: 5px;
            }
            #displayModeOptions input[type="radio"] {
                display: none; /* Hide native radio button */
            }
            #displayModeOptions label.radio-btn {
                display: inline-block;
                padding: 8px 15px;
                border: 1px solid #555;
                border-radius: 5px;
                cursor: pointer;
                background-color: #2c2c2c;
                color: #e0e0e0;
                font-size: 13px;
                transition: background-color 0.2s, border-color 0.2s, color 0.2s;
                user-select: none; /* Prevent text selection */
            }
            #displayModeOptions input[type="radio"]:checked + label.radio-btn {
                background-color: #c15a17; /* Highlight checked button */
                border-color: #c15a17;
                color: #ffffff;
                box-shadow: 0 0 0 2px rgba(193, 90, 23, 0.3);
            }
            #displayModeOptions label.radio-btn:hover {
                background-color: #3a3a3a;
                border-color: #777;
            }
        `);

        // --- UI Elements Creation ---
        const toggleBtn = document.createElement('button');
        toggleBtn.id = 'modelSelectorToggleBtn';
        toggleBtn.innerHTML = '⚙️';
        toggleBtn.title = 'Toggle Model Selector';

        const container = document.createElement('div');
        container.id = 'modelSelectorContainer';

        const header = document.createElement('div');
        header.id = 'modelSelectorHeader';
        const title = document.createElement('span');
        title.textContent = 'Model Settings';
        const closeBtn = document.createElement('button');
        closeBtn.id = 'modelSelectorCloseBtn';
        closeBtn.innerHTML = '×';
        closeBtn.title = 'Close Settings';
        header.appendChild(title);
        header.appendChild(closeBtn);

        const bodyEl = document.createElement('div');
        bodyEl.id = 'modelSelectorBody';

        // --- Display Mode Selection Section ---
        const displayModeSection = document.createElement('div');
        displayModeSection.id = 'displayModeSection';
        const displayModeLabel = document.createElement('label');
        displayModeLabel.textContent = 'Chat Display Mode:';
        const displayModeOptions = document.createElement('div');
        displayModeOptions.id = 'displayModeOptions';

        const createRadioButton = (id, value, text) => {
            const radioInput = document.createElement('input');
            radioInput.type = 'radio';
            radioInput.id = id;
            radioInput.name = 'displayMode'; // All radios in group must have same name
            radioInput.value = value;
            // Checked state set by updateRadioButtons() later

            const radioLabel = document.createElement('label');
            radioLabel.htmlFor = id;
            radioLabel.textContent = text;
            radioLabel.classList.add('radio-btn');

            radioInput.addEventListener('change', (event) => {
                const newMode = event.target.value;
                if (newMode !== currentDisplayMode) {
                    currentDisplayMode = newMode;
                    GM_setValue(STORAGE_KEY_DISPLAY_MODE, currentDisplayMode);
                    console.log(`[Model Selector] Custom mode selection changed to: ${currentDisplayMode}`);
                    ensureModelCompatibility(); // Re-ensure model compatibility after mode change
                    updateModelDropdownOptions(); // Crucial: update model list based on new mode
                    updateStatusDisplay('selected', selectedModel); // Indicate new selection
                }
            });
            displayModeOptions.appendChild(radioInput);
            displayModeOptions.appendChild(radioLabel);
            return radioInput; // Return the input element for global reference
        };

        // Create the 'Normal' and 'Ultra' radio buttons and store references
        normalRadioBtn = createRadioButton('displayModeNormal', 'normal', 'Normal');
        ultraRadioBtn = createRadioButton('displayModeUltra', 'ultra', 'Ultra (Reasoning)');

        displayModeSection.appendChild(displayModeLabel);
        displayModeSection.appendChild(displayModeOptions);
        bodyEl.appendChild(displayModeSection);

        // --- Model Dropdown Section ---
        const modelLabel = document.createElement('label');
        modelLabel.htmlFor = 'modelSelectorDropdown';
        modelLabel.textContent = 'Active AI Model:';
        selectDropdown = document.createElement('select'); // Assign to global variable
        selectDropdown.id = 'modelSelectorDropdown';

        bodyEl.appendChild(modelLabel);
        bodyEl.appendChild(selectDropdown);

        // --- Status Indicator Element ---
        const statusIndicator = document.createElement('div');
        statusIndicator.id = 'modelStatusIndicator';
        bodyEl.appendChild(statusIndicator);

        // --- Assemble Container ---
        container.appendChild(header);
        container.appendChild(bodyEl);

        // --- Append elements to the DOM ---
        function appendElements() {
            if (document.body) {
                document.body.appendChild(toggleBtn);
                document.body.appendChild(container);
                // Now that elements are in DOM, set up the observer and initial UI states
                setupModeObserver(); // This will also call updateRadioButtons and updateModelDropdownOptions
            } else {
                // Fallback for slower DOM loading
                window.addEventListener('DOMContentLoaded', () => {
                    document.body.appendChild(toggleBtn);
                    document.body.appendChild(container);
                    setupModeObserver();
                });
            }
        }
        // Check document state for immediate or delayed appending
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', appendElements);
        } else {
            appendElements();
        }

        // --- Event Listeners for custom UI ---
        toggleBtn.addEventListener('click', () => { container.classList.toggle('visible'); });
        closeBtn.addEventListener('click', () => { container.classList.remove('visible'); });

        selectDropdown.addEventListener('change', (event) => {
            selectedModel = event.target.value;
            GM_setValue(STORAGE_KEY_MODEL, selectedModel);
            console.log(`[Model Selector] Model selection changed to: ${selectedModel}`);

            updateStatusDisplay('selected', selectedModel); // Update status for selection

            // Visual feedback for selection change
            toggleBtn.style.borderColor = '#4caf50'; // Green border on toggle button
            setTimeout(() => { toggleBtn.style.borderColor = '#c15a17'; }, 600); // Revert after a short delay

            selectDropdown.style.transition = 'none'; // Temporarily disable transition for immediate color change
            selectDropdown.style.borderColor = '#4caf50'; // Green border on dropdown
            selectDropdown.style.backgroundColor = '#3a4a3a'; // Darker background
            setTimeout(() => {
                // Re-enable transition and revert styles
                selectDropdown.style.transition = 'border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease';
                selectDropdown.style.borderColor = '#555';
                selectDropdown.style.backgroundColor = '#2c2c2c';
            }, 500);
        });
    }

    // --- Initialize UI ---
    // Ensure UI creation happens after the DOM is ready, or immediately if it already is.
    // setupModeObserver will be called from createEnhancedUI after elements are appended.
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createEnhancedUI);
    } else {
        createEnhancedUI();
    }

})();