- // ==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();
- }
- })();