Character.ai Changer

Combined Background Changer and Color Customizer for Character.AI

// ==UserScript==
// @name         Character.ai Changer
// @namespace    http://tampermonkey
// @version      1.00.1V3
// @description  Combined Background Changer and Color Customizer for Character.AI
// @author       NotYou
// @match        https://character.ai/*
// @match        https://*.character.ai/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // ==========================================
    // SCRIPT 1: Character.AI Background Changer
    // ==========================================

    (function() {
    'use strict';

    // Initialize logging
    console.log('Character.AI Background Changer initialized');

    function log(message) {
        console.log(`[BG Changer]: ${message}`);
    }

    // Create UI elements
    const controlPanel = document.createElement('div');
    const btnToggle = document.createElement('button');
    const statusIndicator = document.createElement('div');

    // Set up control panel
    function setupControlPanel() {
        controlPanel.id = 'cai-bg-control-panel';
        controlPanel.style.position = 'fixed';
        controlPanel.style.bottom = '60px';
        controlPanel.style.right = '20px';
        controlPanel.style.width = '250px';
        controlPanel.style.backgroundColor = 'rgba(30, 30, 30, 0.9)';
        controlPanel.style.color = 'white';
        controlPanel.style.padding = '15px';
        controlPanel.style.borderRadius = '8px';
        controlPanel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
        controlPanel.style.zIndex = '9999999';
        controlPanel.style.fontFamily = 'Arial, sans-serif';
        controlPanel.style.fontSize = '14px';
        controlPanel.style.display = 'none';

        controlPanel.innerHTML = `
            <div id="panel-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
                <h3 style="margin: 0; font-size: 16px;">Background Changer</h3>
                <button id="close-panel" style="background: none; border: none; color: white; cursor: pointer; font-size: 18px;">×</button>
            </div>

            <div id="panel-content">
                <div style="margin-bottom: 15px;">
                    <label for="bg-url" style="display: block; margin-bottom: 5px;">Image URL:</label>
                    <input type="text" id="bg-url" placeholder="https://example.com/image.jpg" style="width: 100%; padding: 5px; box-sizing: border-box; margin-bottom: 5px;">
                    <button id="apply-url" style="padding: 5px 10px; background: #4a4a4a; border: none; color: white; cursor: pointer; border-radius: 3px;">Apply URL</button>
                </div>

                <div style="margin-bottom: 15px;">
                    <label for="bg-upload" style="display: block; margin-bottom: 5px;">Upload Image:</label>
                    <input type="file" id="bg-upload" accept="image/*" style="width: 100%; margin-bottom: 5px;">
                </div>

                <div style="margin-bottom: 15px;">
                    <label for="bg-color" style="display: block; margin-bottom: 5px;">Background Color:</label>
                    <div style="display: flex; align-items: center;">
                        <input type="color" id="bg-color" value="#1a1a1a" style="margin-right: 10px;">
                        <button id="apply-color" style="padding: 5px 10px; background: #4a4a4a; border: none; color: white; cursor: pointer; border-radius: 3px;">Apply Color</button>
                    </div>
                </div>

                <div style="text-align: center; margin-top: 20px;">
                    <button id="reset-bg" style="padding: 8px 15px; background: #cc3333; border: none; color: white; cursor: pointer; border-radius: 3px;">Reset Background</button>
                </div>
            </div>
        `;
    }

    // Set up toggle button
    function setupToggleButton() {
        btnToggle.id = 'cai-bg-toggle';
        btnToggle.textContent = 'Change BG';
        btnToggle.style.position = 'fixed';
        btnToggle.style.bottom = '20px';
        btnToggle.style.right = '20px';
        btnToggle.style.padding = '8px 12px';
        btnToggle.style.backgroundColor = '#3a3';
        btnToggle.style.color = 'white';
        btnToggle.style.border = 'none';
        btnToggle.style.borderRadius = '5px';
        btnToggle.style.fontSize = '14px';
        btnToggle.style.fontWeight = 'bold';
        btnToggle.style.cursor = 'pointer';
        btnToggle.style.zIndex = '9999997';
        btnToggle.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
    }

    // Set up status indicator
    function setupStatusIndicator() {
        statusIndicator.id = 'cai-bg-status';
        statusIndicator.style.position = 'fixed';
        statusIndicator.style.bottom = '20px';
        statusIndicator.style.left = '20px';
        statusIndicator.style.padding = '8px 12px';
        statusIndicator.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        statusIndicator.style.color = 'white';
        statusIndicator.style.borderRadius = '4px';
        statusIndicator.style.fontSize = '14px';
        statusIndicator.style.zIndex = '9999998';
        statusIndicator.style.opacity = '0';
        statusIndicator.style.transition = 'opacity 0.5s';
        statusIndicator.textContent = 'Background changer ready';
    }

    // Toggle panel visibility
    function togglePanel() {
        const isVisible = controlPanel.style.display === 'block';

        if (isVisible) {
            controlPanel.style.display = 'none';
            btnToggle.style.backgroundColor = '#3a3';
        } else {
            controlPanel.style.display = 'block';
            btnToggle.style.backgroundColor = '#555';
        }
    }

    // Show status message
    function showStatus(message, duration = 2000) {
        statusIndicator.textContent = message;
        statusIndicator.style.opacity = '1';

        setTimeout(() => {
            statusIndicator.style.opacity = '0';
        }, duration);
    }

    // Apply background from URL
    function applyUrlBackground() {
        const url = document.getElementById('bg-url').value.trim();

        if (!url) {
            showStatus('Please enter a valid URL');
            return;
        }

        applyBackground(`url(${url}) no-repeat center center fixed`, 'cover');
        showStatus('Background applied from URL');
    }

    // Apply background from color picker
    function applyColorBackground() {
        const color = document.getElementById('bg-color').value;
        applyBackground(color);
        showStatus('Background color applied');
    }

    // Apply background image from file upload
    function handleImageUpload(event) {
        const file = event.target.files[0];

        if (!file) return;

        // Check if file is an image
        if (!file.type.startsWith('image/')) {
            showStatus('Please select an image file');
            return;
        }

        const reader = new FileReader();

        reader.onload = function(e) {
            const imageUrl = e.target.result;
            applyBackground(`url(${imageUrl}) no-repeat center center fixed`, 'cover');
            showStatus('Background image applied');

            // Save the image data to GM storage
            GM_setValue('bgImageData', imageUrl);
        };

        reader.readAsDataURL(file);
    }

    // Apply background
    function applyBackground(style, size = null) {
        document.body.style.background = style;

        if (size) {
            document.body.style.backgroundSize = size;
        }

        // Save settings
        GM_setValue('bgStyle', style);
        GM_setValue('bgSize', size || '');
    }

    // Reset background
    function resetBackground() {
        document.body.style.background = '';
        document.body.style.backgroundSize = '';

        // Clear saved values
        GM_setValue('bgStyle', '');
        GM_setValue('bgSize', '');
        GM_setValue('bgImageData', '');

        // Reset form inputs
        const urlInput = document.getElementById('bg-url');
        const uploadInput = document.getElementById('bg-upload');

        if (urlInput) urlInput.value = '';
        if (uploadInput) uploadInput.value = '';

        showStatus('Background reset to default');
    }

    // Restore saved background
    function restoreBackground() {
        const bgStyle = GM_getValue('bgStyle', '');
        const bgSize = GM_getValue('bgSize', '');

        if (bgStyle) {
            document.body.style.background = bgStyle;

            if (bgSize) {
                document.body.style.backgroundSize = bgSize;
            }

            log('Restored saved background');
        }
    }

    // Setup event listeners
    function setupEventListeners() {
        // Toggle button
        btnToggle.addEventListener('click', togglePanel);

        // Close panel button
        document.getElementById('close-panel')?.addEventListener('click', () => {
            togglePanel();
        });

        // Apply URL background
        document.getElementById('apply-url')?.addEventListener('click', applyUrlBackground);

        // Apply color background
        document.getElementById('apply-color')?.addEventListener('click', applyColorBackground);

        // Handle file upload
        document.getElementById('bg-upload')?.addEventListener('change', handleImageUpload);

        // Reset background
        document.getElementById('reset-bg')?.addEventListener('click', resetBackground);

        log('Event listeners set up');
    }

    // DOM Ready helper function
    function domReady(callback) {
        if (document.readyState === "complete" || document.readyState === "interactive") {
            setTimeout(callback, 1);
        } else {
            document.addEventListener("DOMContentLoaded", callback);
        }
    }

    // Initialize everything when DOM is ready
    function init() {
        if (!document.body) {
            setTimeout(init, 100);
            return;
        }

        try {
            // Setup UI elements
            setupControlPanel();
            setupToggleButton();
            setupStatusIndicator();

            // Add elements to the page
            document.body.appendChild(controlPanel);
            document.body.appendChild(btnToggle);
            document.body.appendChild(statusIndicator);

            // Setup event listeners
            setupEventListeners();

            // Restore saved background
            restoreBackground();

            // Show initial status
            showStatus('Background changer ready');

            log('Initialization complete');
        } catch (error) {
            console.error('[BG Changer Error]:', error);
        }
    }

    // Run initialization when DOM is ready
    domReady(() => {
        init();

        // Additional safety check
        setTimeout(() => {
            if (!document.getElementById('cai-bg-control-panel')) {
                log('Panel not found, retrying initialization');
                init();
            }
        }, 3000);
    });

    // Backup initialization on window load
    window.addEventListener('load', () => {
        if (!document.getElementById('cai-bg-control-panel')) {
            log('Panel not found on window load, reinitializing');
            init();
        }
    });
})();

    // ==========================================
    // SCRIPT 2: Enhanced Color Customizer
    // ==========================================

    (function() {
    'use strict';

    // Helper function to get the current theme
    function getCurrentTheme() {
        return document.documentElement.classList.contains('dark') ? 'dark' : 'light';
    }

    // Default colors based on theme
    function getDefaultColors(theme) {
        const darkColors = {
            'italic': '#E0DF7F',
            'quotationmarks': '#FFFFFF',
            'plaintext': '#A2A2AC',
            'custom': '#E0DF7F',
            'charbubble': '#26272B',
            'userbubble': '#303136',
            'guide': '#131316',
            'input': '#202024',
            'body': '#18181B',
            'accent': '#26272B'
        };

        const lightColors = {
            'italic': '#4F7AA6',
            'quotationmarks': '#000000',
            'plaintext': '#374151',
            'custom': '#4F7AA6',
            'charbubble': '#E4E4E7',
            'userbubble': '#D9D9DF',
            'guide': '#FAFAFA',
            'input': '#F4F4F5',
            'body': '#ECECEE',
            'accent': '#26272B'
        };

        return theme === 'dark' ? darkColors : lightColors;
    }

    // Load Comic Sans MS and other web fonts
    function loadWebFonts() {
        // Add Comic Sans MS
        GM_addStyle(`
            @import url('https://fonts.cdnfonts.com/css/comic-sans');

            @font-face {
                font-family: 'Comic Sans MS';
                src: local('Comic Sans MS'),
                     url('https://fonts.cdnfonts.com/s/11177/comici.woff') format('woff'),
                     url('https://fonts.cdnfonts.com/s/11177/comicbd.woff') format('woff');
                font-display: swap;
            }
        `);

        // Add Google Fonts
        const googleFontsLink = document.createElement('link');
        googleFontsLink.rel = 'stylesheet';
        googleFontsLink.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&family=Open+Sans:wght@400;700&family=Lato:wght@400;700&family=Montserrat:wght@400;700&display=swap';
        document.head.appendChild(googleFontsLink);
    }

    // Create main customization menu
    function createCustomizationMenu() {
        const theme = getCurrentTheme();
        const menuContainer = document.createElement('div');
        menuContainer.id = 'cai-color-customizer';
        menuContainer.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 900px;
            background-color: ${theme === 'dark' ? 'rgba(19, 19, 22, 0.95)' : 'rgba(214, 214, 221, 0.95)'};
            border-radius: 10px;
            padding: 20px;
            z-index: 9999;
            display: none;
        `;

        // Tabs container
        const tabContainer = document.createElement('div');
        tabContainer.style.cssText = `
            display: flex;
            margin-bottom: 15px;
        `;

        // Content container
        const contentContainer = document.createElement('div');

        // Create tabs
        const tabs = [
            { name: 'Colors', id: 'colors' },
            { name: 'Typography', id: 'typography' },
            { name: 'Layout', id: 'layout' }
        ];

        tabs.forEach(tab => {
            const tabButton = document.createElement('button');
            tabButton.textContent = tab.name;
            tabButton.style.cssText = `
                flex-grow: 1;
                padding: 10px;
                background-color: ${theme === 'dark' ? '#26272B' : '#E4E4E7'};
                border: none;
                margin-right: 5px;
                cursor: pointer;
            `;

            tabButton.addEventListener('click', () => {
                // Hide all content
                contentContainer.innerHTML = '';

                // Show specific tab content
                switch(tab.id) {
                    case 'colors':
                        renderColorTab(contentContainer);
                        break;
                    case 'typography':
                        renderTypographyTab(contentContainer);
                        break;
                    case 'layout':
                        renderLayoutTab(contentContainer);
                        break;
                }
            });

            tabContainer.appendChild(tabButton);
        });

        // Add close button
        const closeButton = document.createElement('button');
        closeButton.textContent = '×';
        closeButton.style.cssText = `
            position: absolute;
            top: 10px;
            right: 10px;
            background: none;
            border: none;
            font-size: 20px;
            cursor: pointer;
        `;
        closeButton.addEventListener('click', () => {
            menuContainer.style.display = 'none';
        });

        menuContainer.appendChild(closeButton);
        menuContainer.appendChild(tabContainer);
        menuContainer.appendChild(contentContainer);

        document.body.appendChild(menuContainer);

        // Auto-select the first tab on creation
        tabContainer.firstChild.click();

        return menuContainer;
    }

    // Render color customization tab
    function renderColorTab(container) {
        const theme = getCurrentTheme();
        const categories = ['italic', 'quotationmarks', 'plaintext', 'custom', 'charbubble', 'userbubble', 'guide', 'input', 'body', 'accent'];

        categories.forEach(category => {
            const colorWrapper = document.createElement('div');
            colorWrapper.style.cssText = `
                display: flex;
                align-items: center;
                margin-bottom: 10px;
            `;

            const label = document.createElement('label');
            label.textContent = category.charAt(0).toUpperCase() + category.slice(1);
            label.style.marginRight = '10px';

            const colorPicker = document.createElement('input');
            colorPicker.type = 'color';
            colorPicker.value = GM_getValue(`${category}_color`, getDefaultColors(theme)[category]);

            colorPicker.addEventListener('input', () => {
                GM_setValue(`${category}_color`, colorPicker.value);
                applyCustomColors();
            });

            colorWrapper.appendChild(label);
            colorWrapper.appendChild(colorPicker);
            container.appendChild(colorWrapper);
        });
    }

    // Render typography tab
    function renderTypographyTab(container) {
        // Typography container
        const typographyContainer = document.createElement('div');
        typographyContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 15px;
        `;

        // Font selector
        const fontSelectContainer = document.createElement('div');
        fontSelectContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 5px;
        `;

        const fontSelectLabel = document.createElement('label');
        fontSelectLabel.textContent = 'Font Family:';

        const fontSelect = document.createElement('select');
        fontSelect.style.width = '100%';

        // Common fonts including Comic Sans MS
        const fonts = [
            'Comic Sans MS', 'Inter', 'Arial', 'Helvetica', 'Verdana', 'Tahoma',
            'Times New Roman', 'Georgia', 'Garamond',
            'Courier New', 'Consolas', 'Monaco',
            'Roboto', 'Open Sans', 'Lato', 'Montserrat'
        ];

        // Add default option
        const defaultOption = document.createElement('option');
        defaultOption.value = '';
        defaultOption.textContent = 'Default';
        fontSelect.appendChild(defaultOption);

        // Add font options
        fonts.forEach(font => {
            const option = document.createElement('option');
            option.value = font;
            option.textContent = font;
            option.style.fontFamily = font;
            fontSelect.appendChild(option);
        });

        // Set selected value from saved preference
        fontSelect.value = GM_getValue('selected_font', '');

        fontSelect.addEventListener('change', () => {
            GM_setValue('selected_font', fontSelect.value);
            applyTypographySettings();
        });

        fontSelectContainer.appendChild(fontSelectLabel);
        fontSelectContainer.appendChild(fontSelect);
        typographyContainer.appendChild(fontSelectContainer);

        // Font Style
        const fontStyleContainer = document.createElement('div');
        fontStyleContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 5px;
            margin-top: 10px;
        `;

        const fontStyleLabel = document.createElement('label');
        fontStyleLabel.textContent = 'Font Style:';

        const fontStyleOptions = document.createElement('div');
        fontStyleOptions.style.cssText = `
            display: flex;
            gap: 10px;
        `;

        // Bold checkbox
        const boldContainer = document.createElement('div');
        const boldCheckbox = document.createElement('input');
        boldCheckbox.type = 'checkbox';
        boldCheckbox.id = 'font-bold';
        boldCheckbox.checked = GM_getValue('fontBold', false);

        const boldLabel = document.createElement('label');
        boldLabel.textContent = 'Bold';
        boldLabel.htmlFor = 'font-bold';

        boldCheckbox.addEventListener('change', () => {
            GM_setValue('fontBold', boldCheckbox.checked);
            applyTypographySettings();
        });

        boldContainer.appendChild(boldCheckbox);
        boldContainer.appendChild(boldLabel);

        // Italic checkbox
        const italicContainer = document.createElement('div');
        const italicCheckbox = document.createElement('input');
        italicCheckbox.type = 'checkbox';
        italicCheckbox.id = 'font-italic';
        italicCheckbox.checked = GM_getValue('fontItalic', false);

        const italicLabel = document.createElement('label');
        italicLabel.textContent = 'Italic';
        italicLabel.htmlFor = 'font-italic';

        italicCheckbox.addEventListener('change', () => {
            GM_setValue('fontItalic', italicCheckbox.checked);
            applyTypographySettings();
        });

        italicContainer.appendChild(italicCheckbox);
        italicContainer.appendChild(italicLabel);

        fontStyleOptions.appendChild(boldContainer);
        fontStyleOptions.appendChild(italicContainer);
        fontStyleContainer.appendChild(fontStyleLabel);
        fontStyleContainer.appendChild(fontStyleOptions);
        typographyContainer.appendChild(fontStyleContainer);

        // Font Size Slider
        const fontSizeContainer = document.createElement('div');
        fontSizeContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 5px;
        `;

        const fontSizeLabel = document.createElement('label');
        fontSizeLabel.textContent = 'Font Size: ' + GM_getValue('fontSize', 16) + 'px';

        const fontSizeSlider = document.createElement('input');
        fontSizeSlider.type = 'range';
        fontSizeSlider.min = 10;
        fontSizeSlider.max = 30;
        fontSizeSlider.value = GM_getValue('fontSize', 16);
        fontSizeSlider.style.width = '100%';

        fontSizeSlider.addEventListener('input', () => {
            const newSize = fontSizeSlider.value;
            fontSizeLabel.textContent = 'Font Size: ' + newSize + 'px';
            GM_setValue('fontSize', newSize);
            applyTypographySettings();
        });

        fontSizeContainer.appendChild(fontSizeLabel);
        fontSizeContainer.appendChild(fontSizeSlider);
        typographyContainer.appendChild(fontSizeContainer);

        container.appendChild(typographyContainer);
    }

    // Render layout tab
    function renderLayoutTab(container) {
        const layoutContainer = document.createElement('div');
        layoutContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 15px;
        `;

        // Avatar size control
        const avatarSizeContainer = document.createElement('div');
        avatarSizeContainer.style.cssText = `
            display: flex;
            align-items: center;
            gap: 10px;
        `;

        const sizeLabel = document.createElement('label');
        sizeLabel.textContent = 'Avatar Image Size (px):';

        const sizeInput = document.createElement('input');
        sizeInput.type = 'number';
        sizeInput.value = GM_getValue('image_size', '24');
        sizeInput.min = '16';
        sizeInput.max = '64';
        sizeInput.style.width = '60px';

        sizeInput.addEventListener('change', () => {
            GM_setValue('image_size', sizeInput.value);
            applyLayoutSettings();
        });

        avatarSizeContainer.appendChild(sizeLabel);
        avatarSizeContainer.appendChild(sizeInput);
        layoutContainer.appendChild(avatarSizeContainer);

        // Chat bubble spacing control
        const spacingContainer = document.createElement('div');
        spacingContainer.style.cssText = `
            display: flex;
            align-items: center;
            gap: 10px;
        `;

        const spacingLabel = document.createElement('label');
        spacingLabel.textContent = 'Message Spacing (px):';

        const spacingInput = document.createElement('input');
        spacingInput.type = 'number';
        spacingInput.value = GM_getValue('message_spacing', '10');
        spacingInput.min = '0';
        spacingInput.max = '30';
        spacingInput.style.width = '60px';

        spacingInput.addEventListener('change', () => {
            GM_setValue('message_spacing', spacingInput.value);
            applyLayoutSettings();
        });

        spacingContainer.appendChild(spacingLabel);
        spacingContainer.appendChild(spacingInput);
        layoutContainer.appendChild(spacingContainer);

        container.appendChild(layoutContainer);
    }

    // Apply color customizations dynamically
    function applyCustomColors() {
        const theme = getCurrentTheme();
        const defaultColors = getDefaultColors(theme);
        const categories = ['italic', 'quotationmarks', 'plaintext', 'custom', 'charbubble', 'userbubble', 'guide', 'input', 'body', 'accent'];

        // Remove any existing style element
        const existingStyle = document.getElementById('cai-custom-colors');
        if (existingStyle) {
            existingStyle.remove();
        }

        const styleElement = document.createElement('style');
        styleElement.id = 'cai-custom-colors';
        let css = '';

        categories.forEach(category => {
            const color = GM_getValue(`${category}_color`, defaultColors[category]);

            switch(category) {
                case 'italic':
                    css += `em { color: ${color} !important; } `;
                    break;
                case 'quotationmarks':
                    css += `blockquote { color: ${color} !important; } `;
                    break;
                case 'plaintext':
                    css += `p[node='[object Object]'] { color: ${color} !important; } `;
                    break;
                case 'charbubble':
                    css += `.mt-1.bg-surface-elevation-2 { background-color: ${color} !important; } `;
                    break;
                case 'userbubble':
                    css += `.mt-1.bg-surface-elevation-3 { background-color: ${color} !important; } `;
                    break;
                case 'guide':
                    css += `.overflow-y-auto { background-color: ${color} !important; } `;
                    break;
                case 'input':
                    css += `.w-full.border-none.bg-surface-elevation-1 { background-color: ${color} !important; } `;
                    break;
                case 'body':
                    css += `body { background-color: ${color} !important; } `;
                    break;
                case 'accent':
                    css += `.text-primary-600 { color: ${color} !important; } `;
                    break;
                case 'custom':
                    css += `code, pre { color: ${color} !important; } `;
                    break;
            }
        });

        styleElement.textContent = css;
        document.head.appendChild(styleElement);
    }

    // Apply typography settings
    function applyTypographySettings() {
        // Remove any existing style element
        const existingStyle = document.getElementById('cai-custom-typography');
        if (existingStyle) {
            existingStyle.remove();
        }

        const font = GM_getValue('selected_font', '');
        const fontSize = GM_getValue('fontSize', 16) + 'px';
        const isBold = GM_getValue('fontBold', false);
        const isItalic = GM_getValue('fontItalic', false);

        const fontWeight = isBold ? 'bold' : 'normal';
        const fontStyle = isItalic ? 'italic' : 'normal';

        const styleElement = document.createElement('style');
        styleElement.id = 'cai-custom-typography';

        // Make sure we properly handle Comic Sans MS and other fonts with spaces
        const fontFamily = font ? `'${font}', ` : '';

        styleElement.textContent = `
            p, textarea, button, div.text-sm, .markdown-content, .message-content,
            div[class*="text-base"], div[class*="text-md"], div[class*="text-lg"],
            span[class*="text-base"], span[class*="text-md"], span[class*="text-lg"] {
                font-family: ${fontFamily}'Noto Sans', sans-serif !important;
                font-size: ${fontSize} !important;
                font-weight: ${fontWeight} !important;
                font-style: ${fontStyle} !important;
            }
        `;
        document.head.appendChild(styleElement);
    }

    // Apply layout settings
    function applyLayoutSettings() {
        // Remove any existing style element
        const existingStyle = document.getElementById('cai-custom-layout');
        if (existingStyle) {
            existingStyle.remove();
        }

        const imageSize = GM_getValue('image_size', '24') + 'px';
        const messageSpacing = GM_getValue('message_spacing', '10') + 'px';

        const styleElement = document.createElement('style');
        styleElement.id = 'cai-custom-layout';

        styleElement.textContent = `
            .mt-0.hidden.md\\:flex.flex-col.gap-3.items-center img {
                width: ${imageSize} !important;
                height: ${imageSize} !important;
            }
            .mt-1.flex.w-full {
                margin-bottom: ${messageSpacing} !important;
            }
        `;
        document.head.appendChild(styleElement);
    }

    // Apply all settings at once
    function applySettings() {
        applyCustomColors();
        applyTypographySettings();
        applyLayoutSettings();
    }

    // Create global customization button
    function createCustomizationButton() {
        // Check if button already exists
        if (document.getElementById('cai-customizer-button')) {
            return;
        }

        const button = document.createElement('button');
        button.id = 'cai-customizer-button';
        button.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            width: 24px;
            height: 24px;
            background-image: url('https://i.imgur.com/yBgJ3za.png');
            background-size: cover;
            z-index: 9998;
            border: none;
            cursor: pointer;
            border-radius: 4px;
        `;

        // Create menu only if it doesn't exist
        let menu = document.getElementById('cai-color-customizer');
        if (!menu) {
            menu = createCustomizationMenu();
        }

        button.addEventListener('click', () => {
            menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
        });

        document.body.appendChild(button);
    }

    // Global keyboard shortcut handler
    function handleKeyboardShortcut(event) {
        if (event.key === '`' && event.ctrlKey) {
            event.preventDefault();
            const menu = document.getElementById('cai-color-customizer');
            if (menu) {
                menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
            }
        }
    }

    // Wait for page to load before initialization
    function waitForPageLoad() {
        // Load web fonts first
        loadWebFonts();

        // Check if we're on Character.AI or Discord
        if (window.location.hostname.includes('character.ai')) {
            // For Character.AI
            if (document.readyState === 'complete' || document.readyState === 'interactive') {
                initializeForCAI();
            } else {
                window.addEventListener('DOMContentLoaded', initializeForCAI);
            }
        } else if (window.location.hostname.includes('discord.com')) {
            // For Discord
            const checkInterval = setInterval(() => {
                if (document.querySelector('#app-mount')) {
                    clearInterval(checkInterval);
                    initializeForDiscord();
                }
            }, 500);
        }
    }

    // Initialize for Character.AI
    function initializeForCAI() {
        // Ensure the button is created
        createCustomizationButton();

        // Apply settings
        applySettings();

        // Add keyboard shortcut listener
        document.addEventListener('keydown', handleKeyboardShortcut);

        // Re-apply settings when theme changes
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.attributeName === 'class' &&
                    mutation.target === document.documentElement) {
                    applySettings();
                }
            });
        });

        observer.observe(document.documentElement, { attributes: true });

        // Make sure button stays visible with periodic check
        setInterval(() => {
            if (!document.getElementById('cai-customizer-button')) {
                createCustomizationButton();
            }
        }, 5000);
    }

    // Initialize for Discord
    function initializeForDiscord() {
        // Apply font settings if exists
        const savedFont = GM_getValue('discordFont', GM_getValue('selected_font', ''));
        const fontSize = GM_getValue('discordFontSize', GM_getValue('fontSize', 16));
        const fontWeight = GM_getValue('discordFontWeight', GM_getValue('fontBold', false) ? 'bold' : 'normal');
        const fontStyle = GM_getValue('fontItalic', false) ? 'italic' : 'normal';
        const fontColor = GM_getValue('discordFontColor', '#ffffff');

        // Create style for Discord font customization
        const styleElement = document.createElement('style');
        styleElement.id = 'discord-font-customizer';
        styleElement.textContent = `
            .markup-eYLPri, .contents-2MsGLg, [class*="messageContent-"], [class*="channelName-"] {
                font-family: ${savedFont ? `'${savedFont}',` : ''} 'Whitney', 'Helvetica Neue', Helvetica, Arial, sans-serif !important;
                font-size: ${fontSize}px !important;
                font-weight: ${fontWeight} !important;
                font-style: ${fontStyle} !important;
                color: ${fontColor} !important;
            }
        `;
        document.head.appendChild(styleElement);

        // Create customization button for Discord
        createCustomizationButton();

        // Add keyboard shortcut listener for Discord
        document.addEventListener('keydown', handleKeyboardShortcut);

        // Make sure button stays visible with periodic check
        setInterval(() => {
            if (!document.getElementById('cai-customizer-button')) {
                createCustomizationButton();
            }
        }, 5000);
    }

    // Start initialization
    waitForPageLoad();

    // Fallback initialization to ensure script runs
    setTimeout(() => {
        if (!document.getElementById('cai-customizer-button')) {
            loadWebFonts();
            createCustomizationButton();
            applySettings();
            document.addEventListener('keydown', handleKeyboardShortcut);
        }
    }, 3000);
})();


    // ==========================================
    // SHARED INITIALIZATION
    // ==========================================

    function initializeAll() {
        // Call the initialization functions from both scripts if they exist
        // For example:
        // if (typeof initBackgroundChanger === 'function') {
        //     initBackgroundChanger();
        // }
        //
        // if (typeof initColorCustomizer === 'function') {
        //     initColorCustomizer();
        // }
    }

    // Run on page load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeAll);
    } else {
        initializeAll();
    }
})();

QingJ © 2025

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