c.ai Enhanced Color Customizer

Enhanced color and UI customization for Character.AI

Verze ze dne 05. 03. 2025. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name        c.ai Enhanced Color Customizer
// @namespace   cai-color-customizer
// @match       https://character.ai/*
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
// @license     MIT
// @version     1.0
// @description Enhanced color and UI customization for Character.AI
// @icon        https://i.imgur.com/ynjBqKW.png
// ==/UserScript==

(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;
    }

    // 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);
        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) {
        // Font selection, size, weight etc.
        const fonts = [
            { name: 'Inter', value: '__Inter_918210' },
            { name: 'Onest', value: '__Onest_b2ce1d' },
            { name: 'Noto Sans', value: 'Noto Sans' },
            { name: 'Arial', value: 'Arial' }
        ];

        const fontSelect = document.createElement('select');
        fonts.forEach(font => {
            const option = document.createElement('option');
            option.value = font.value;
            option.text = font.name;
            fontSelect.appendChild(option);
        });

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

        container.appendChild(fontSelect);
    }

    // Render layout tab
    function renderLayoutTab(container) {
        // Image size, margins, etc.
        const sizeInput = document.createElement('input');
        sizeInput.type = 'number';
        sizeInput.value = GM_getValue('image_size', '24');
        sizeInput.addEventListener('change', () => {
            GM_setValue('image_size', sizeInput.value);
            applyLayoutSettings();
        });

        container.appendChild(sizeInput);
    }

    // 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'];

        const styleElement = document.createElement('style');
        let css = '';

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

            switch(category) {
                case 'italic':
                    css += `em { 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}; } `;
                    break;
                case 'userbubble':
                    css += `.mt-1.bg-surface-elevation-3 { background-color: ${color}; } `;
                    break;
                // Add more specific color mappings as needed
            }
        });

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

    // Apply typography settings
    function applyTypographySettings() {
        const font = GM_getValue('selected_font', '__Inter_918210');
        const styleElement = document.createElement('style');
        styleElement.textContent = `
            p, textarea, button, div.text-sm {
                font-family: '${font}', 'Noto Sans', sans-serif !important;
            }
        `;
        document.head.appendChild(styleElement);
    }

    // Apply layout settings
    function applyLayoutSettings() {
        const imageSize = GM_getValue('image_size', '24') + 'px';
        const styleElement = document.createElement('style');
        styleElement.textContent = `
            .mt-0.hidden.md\\:flex.flex-col.gap-3.items-center img {
                width: ${imageSize} !important;
                height: ${imageSize} !important;
            }
        `;
        document.head.appendChild(styleElement);
    }

    // Global customization button
    function createCustomizationButton() {
        const button = document.createElement('button');
        button.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            width: 22px;
            height: 22px;
            background-image: url('https://i.imgur.com/yBgJ3za.png');
            background-size: cover;
            z-index: 9998;
            border: none;
        `;

        const 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';
            }
        }
    }

    // Initialize customizations on page load
    function initialize() {
        createCustomizationButton();
        applyCustomColors();
        applyTypographySettings();
        applyLayoutSettings();

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

    // Run initialization
    initialize();
})();