TrixBox

TriX Executor's ChatBox (for territorial.io)!

目前為 2025-11-16 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         TrixBox
// @namespace    http://tampermonkey.net/
// @version      0.3.6
// @description  TriX Executor's ChatBox (for territorial.io)!
// @author       Painsel
// @match        https://territorial.io/*
// @match        https://fxclient.github.io/FXclient/*
// @match        https://discord.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// ==/UserScript==
 
/*
  Copyright (c) 2025 Painsel
  All Rights Reserved.
 
  This script is proprietary software. You may not use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software.
 
  UNAUTHORIZED COPYING, DISTRIBUTION, OR MODIFICATION OF THIS SCRIPT,
  EITHER IN WHOLE OR IN PART, IS STRICTLY PROHIBITED.
*/
 
(function() {
    'use strict';
 
    // --- Theme Colors (for UI elements outside the iframe) ---
    const theme = {
        icon: '#2f3136',
        modalBg: '#2f3136',
        text: '#dcddde',
        buttonPrimary: '#5865f2',
        buttonSecondary: '#4f545c'
    };
 
    const SCRIPT_UPDATE_URL = 'https://update.greasyfork.org/scripts/555536/TrixBox.meta.js';
    const SCRIPT_INSTALL_URL = 'https://update.greasyfork.org/scripts/555536/TrixBox.user.js';
 
    // --- 1. UPDATE CHECKING LOGIC ---
 
    const showUpdateModal = () => {
        const modalStyle = `
            .trixbox-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 100000; display: flex; align-items: center; justify-content: center; }
            .trixbox-modal-content { background: ${theme.modalBg}; color: ${theme.text}; padding: 25px; border-radius: 10px; text-align: center; font-family: sans-serif; box-shadow: 0 5px 15px rgba(0,0,0,0.5); }
            .trixbox-modal-content h2 { margin-top: 0; }
            .trixbox-modal-content p { margin: 15px 0; }
            .trixbox-modal-btn { color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 0 10px; font-size: 14px; }
            .trixbox-modal-btn.primary { background: ${theme.buttonPrimary}; }
            .trixbox-modal-btn.secondary { background: ${theme.buttonSecondary}; }
        `;
        const styleSheet = document.createElement("style");
        styleSheet.innerText = modalStyle;
        document.head.appendChild(styleSheet);
 
        const modalOverlay = document.createElement('div');
        modalOverlay.className = 'trixbox-modal-overlay';
        modalOverlay.innerHTML = `
            <div class="trixbox-modal-content">
                <h2>OUTDATED VERSION</h2>
                <p>You are using an Outdated version of TrixBox.<br>Please update for the best experience!</p>
                <button id="trixbox-update-btn" class="trixbox-modal-btn primary">Update Now!</button>
                <button id="trixbox-later-btn" class="trixbox-modal-btn secondary">Remind Me Later!</button>
            </div>
        `;
        document.body.appendChild(modalOverlay);
 
        document.getElementById('trixbox-update-btn').onclick = () => { window.location.href = SCRIPT_INSTALL_URL; };
        document.getElementById('trixbox-later-btn').onclick = () => { modalOverlay.remove(); };
    };
 
    const checkForUpdates = () => {
        try {
            const localVersion = GM_info.script.version;
            GM_xmlhttpRequest({
                method: 'GET',
                url: SCRIPT_UPDATE_URL,
                onload: function(response) {
                    if (response.status !== 200) return;
                    const remoteVersionMatch = response.responseText.match(/@version\s+([0-9.]+)/);
                    if (remoteVersionMatch && remoteVersionMatch[1]) {
                        if (remoteVersionMatch[1] > localVersion) {
                            showUpdateModal();
                        }
                    }
                }
            });
        } catch (e) { console.error('TrixBox: Update check failed.', e); }
    };
 
    // --- 2. CHATTABLE CUSTOM COMMANDS ---
    // Initialize custom commands object before chat loads
    window.chattable = window.chattable || {};
    window.chattable.commands = {
        cf: function() {
            const result = Math.random() < 0.5 ? 'Heads' : 'Tails';
            if (typeof chattable !== 'undefined' && chattable.sendMessage) {
                chattable.sendMessage(`🪙 Coinflip Result: **${result}**`);
            }
        },
        coinflip: function() {
            const result = Math.random() < 0.5 ? 'Heads' : 'Tails';
            if (typeof chattable !== 'undefined' && chattable.sendMessage) {
                chattable.sendMessage(`🪙 Coinflip Result: **${result}**`);
            }
        },
        userinfo: function() {
            if (typeof chattable !== 'undefined' && chattable.sendMessage) {
                chattable.sendMessage('📊 User info command executed!');
            }
        },
        stats: function(fullstring) {
            if (typeof chattable !== 'undefined' && chattable.sendMessage) {
                chattable.sendMessage('📈 Stats: Chat is fully loaded and operational!');
            }
        }
    };

    // --- 2A. THEME STORAGE AND SELECTOR ---
    const themes = [
        'Amber', 'Amdroid', 'Comment Section', 'Confidential', 'Glass',
        'Hacker Terminal', 'Kick', 'Moderno', 'Notepad', 'Pastel Pink',
        'Retrowave Red', 'Tendo', 'Wannabe XP', 'Professional', 'Bytechat'
    ];

    let currentTheme = localStorage.getItem('trixbox-theme') || 'Tendo';
    let notificationSound = localStorage.getItem('trixbox-notification-sound') || 'default';
    let customCSSEnabled = localStorage.getItem('trixbox-custom-css') === 'true';

    const applyTheme = (themeName) => {
        localStorage.setItem('trixbox-theme', themeName);
        currentTheme = themeName;
        // Defer initialization until library is loaded
        setTimeout(() => {
            if (typeof chattable !== 'undefined' && chattable.initialize) {
                chattable.initialize({
                    theme: themeName.toLowerCase().replace(/\s+/g, ''),
                    stylesheet: customCSSEnabled ? localStorage.getItem('trixbox-custom-styles') : undefined
                });
            }
        }, 100);
    };

    // CSS Styling configuration
    const customStyleManager = {
        getDefaultStyles: () => `
/* TrixBox Default Styles */
:root {
  --notification-sfx: ${notificationSound === 'none' ? 'none' : 'default'};
  --advanced-layout: true;
}

/* Message styling */
.allMessages {
  padding: 8px;
  border-radius: 4px;
  margin: 4px 0;
}

.allMessages.sent {
  background: rgba(88, 101, 242, 0.1);
}

.allMessages.received {
  background: rgba(79, 84, 92, 0.1);
}

/* Badge styling */
.mod, .owner, .beta {
  border-radius: 3px;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: bold;
}

/* Input styling */
#input {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 14px;
}

#input:empty::before {
  content: "Type a message...";
  color: #72767d;
}

/* Settings and UI elements */
#settings {
  opacity: 0.8;
  transition: opacity 0.2s;
}

#settings:hover {
  opacity: 1;
}

/* Timestamp styling */
#timestamp {
  font-size: 11px;
  color: #72767d;
}

/* Reply styling */
.reply {
  border-left: 3px solid #5865f2;
  padding-left: 8px;
  opacity: 0.8;
}

/* Context menu */
.ctxMenuOption {
  padding: 8px 12px;
  border-radius: 4px;
  transition: background 0.15s;
}

.ctxMenuOption:hover {
  background: #5865f2;
}

/* Emoji styling */
.emoji {
  transition: transform 0.2s;
}

.emoji:hover {
  transform: scale(1.2);
}

/* Load more button */
#loadMore {
  background: #5865f2;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.2s;
}

#loadMore:hover {
  background: #4752c4;
}

/* Scroll to bottom button */
#scrollToBottom {
  background: #5865f2;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

/* Pinned messages */
.pinned {
  background: rgba(88, 101, 242, 0.2);
  border-left: 4px solid #5865f2;
  padding: 8px;
}

/* Highlight mentions */
.highlight {
  background: #faa61a;
  padding: 0 4px;
  border-radius: 3px;
  font-weight: bold;
}

/* Reply banner */
#replyBanner {
  background: #2f3136;
  border-top: 2px solid #5865f2;
  padding: 8px 12px;
  font-size: 13px;
}

/* Emoji tray */
#emojiTray {
  background: #2f3136;
  border: 1px solid #202225;
}

.emojiInGrid {
  padding: 6px;
  border-radius: 4px;
  cursor: pointer;
  transition: transform 0.1s;
}

.emojiInGrid:hover {
  transform: scale(1.15);
}

#emojiTrayToggle {
  opacity: 0.8;
  transition: opacity 0.2s;
}

#emojiTrayToggle:hover {
  opacity: 1;
}

/* Typing indicator */
#is_typing {
  font-style: italic;
  color: #72767d;
}

/* Tutorial */
#shadow {
  display: none;
}

/* Settings menu */
#settingsWindow {
  background: #2f3136;
}

#settingsMenu {
  background: #36393f;
  color: #dcddde;
}

#account, #name, #dltranscript, #save, #closeSettingsBtn {
  background: #5865f2;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.2s;
}

#account:hover, #name:hover, #dltranscript:hover, #save:hover, #closeSettingsBtn:hover {
  background: #4752c4;
}

/* Dialogue modal */
.dialogueModal {
  background: #36393f;
  color: #dcddde;
  border-radius: 8px;
  padding: 20px;
}

/* Background */
#background {
  background-color: #36393f;
}

/* Top banner */
#top_banner {
  background: linear-gradient(135deg, #5865f2, #4752c4);
}
        `,
        
        applyCustomStyles: (css) => {
            localStorage.setItem('trixbox-custom-styles', css);
            // Defer initialization until library is loaded
            setTimeout(() => {
                if (typeof chattable !== 'undefined' && chattable.initialize) {
                    chattable.initialize({
                        stylesheet: css
                    });
                }
            }, 100);
        },
        
        resetStyles: () => {
            localStorage.removeItem('trixbox-custom-styles');
            customCSSEnabled = false;
            localStorage.setItem('trixbox-custom-css', 'false');
            applyTheme(currentTheme);
        }
    };

    // --- 2B. PROFILE PICTURES STORAGE ---
    let profilePictures = JSON.parse(localStorage.getItem('trixbox-profiles')) || {};

    const setProfilePicture = (userId, imageUrl) => {
        profilePictures[userId] = imageUrl;
        localStorage.setItem('trixbox-profiles', JSON.stringify(profilePictures));
    };

    const getProfilePicture = (userId) => {
        return profilePictures[userId] || null;
    };

    // --- 2C. EMBEDS STORAGE ---
    let embeds = JSON.parse(localStorage.getItem('trixbox-embeds')) || {};

    const addEmbed = (messageId, embedData) => {
        embeds[messageId] = embedData;
        localStorage.setItem('trixbox-embeds', JSON.stringify(embeds));
    };

    const getEmbed = (messageId) => {
        return embeds[messageId] || null;
    };

    // --- 2D. FILE ATTACHMENTS STORAGE ---
    let attachments = JSON.parse(localStorage.getItem('trixbox-attachments')) || {};

    const addAttachment = (messageId, fileData) => {
        attachments[messageId] = fileData;
        localStorage.setItem('trixbox-attachments', JSON.stringify(attachments));
    };

    const getAttachment = (messageId) => {
        return attachments[messageId] || null;
    };

    // --- 2E. CHANGELOG MODAL ---
    const showChangelogModal = () => {
        const changelog = `
            <h2>TrixBox v0.3.2 - Changelog</h2>
            <div style="text-align: left; font-size: 13px; line-height: 1.6;">
                <h3 style="color: #faa61a;">🔧 v0.3.2 Bug Fixes & Improvements:</h3>
                <ul>
                    <li><strong>Fixed Chattable Initialization Error:</strong> Resolved "chattable.initialize is not a function" error</li>
                    <li><strong>Improved Library Loading:</strong> Added retry mechanism with exponential backoff</li>
                    <li><strong>Better Error Handling:</strong> Deferred initialization prevents timing issues</li>
                    <li><strong>Theme Loading Optimization:</strong> Themes now load reliably on first attempt</li>
                    <li><strong>Custom Styles Support:</strong> CSS application now works correctly with library lifecycle</li>
                </ul>
                <h3 style="color: #faa61a;">🆕 v0.3.1 Updates:</h3>
                <ul>
                    <li><strong>Improved Settings Modal:</strong> Better organization with expandable sections</li>
                    <li><strong>Enhanced CSS Editor:</strong> Syntax highlighting for CSS with better textarea</li>
                    <li><strong>Chattable API Integration:</strong> Full support for custom commands and themes</li>
                    <li><strong>Sound Testing:</strong> Preview notification sounds before saving</li>
                    <li><strong>Settings Info Panel:</strong> View current theme, CSS status, and notification settings</li>
                </ul>
                <h3>✨ v0.3.0 Features:</h3>
                <ul>
                    <li><strong>!cf / !coinflip Command:</strong> Flip a coin in chat with !cf or !coinflip</li>
                    <li><strong>Custom Commands API:</strong> Integrated Chattable API for custom command system</li>
                    <li><strong>Custom Profile Pictures:</strong> Set custom profile pictures for users</li>
                    <li><strong>Embeds Support:</strong> Send rich embeds in messages</li>
                    <li><strong>File Attachments:</strong> Attach files with .p4 and .mp3 player support</li>
                    <li><strong>Custom Themes:</strong> Choose from 15 unique themes (Amber, Glass, Hacker Terminal, Kick, Moderno, Notepad, Pastel Pink, Retrowave Red, Tendo, Wannabe XP, Professional, Bytechat, etc)</li>
                    <li><strong>Advanced CSS Styling:</strong> Edit and apply custom CSS styles directly in settings</li>
                    <li><strong>Notification Sound Control:</strong> Customize or disable notification sounds</li>
                </ul>
                <h3>🔧 Improvements:</h3>
                <ul>
                    <li>Integrated Chattable Library with custom command support</li>
                    <li>Added persistent theme selection using localStorage</li>
                    <li>Added profile picture and embed storage system</li>
                    <li>Enhanced chat functionality with command parsing and API integration</li>
                    <li>Added comprehensive default CSS styles for all chat elements</li>
                    <li>Settings modal now includes advanced customization options</li>
                </ul>
                <h3>📝 Technical:</h3>
                <ul>
                    <li>Version bumped from 0.2.9 to 0.3.1</li>
                    <li>Chattable API fully integrated for theme and custom command handling</li>
                    <li>localStorage optimization for better performance</li>
                </ul>
            </div>
        `;
        
        const modalStyle = `
            .trixbox-changelog-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 100001; display: flex; align-items: center; justify-content: center; }
            .trixbox-changelog-content { background: ${theme.modalBg}; color: ${theme.text}; padding: 30px; border-radius: 10px; text-align: center; font-family: sans-serif; box-shadow: 0 5px 15px rgba(0,0,0,0.5); max-width: 500px; max-height: 600px; overflow-y: auto; }
            .trixbox-changelog-content h2 { margin-top: 0; color: ${theme.buttonPrimary}; }
            .trixbox-changelog-content h3 { margin: 20px 0 10px 0; color: ${theme.buttonPrimary}; font-size: 15px; }
            .trixbox-changelog-content ul { list-style-position: inside; margin: 10px 0; padding: 0; }
            .trixbox-changelog-content li { margin: 5px 0; text-align: left; }
            .trixbox-changelog-btn { color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 10px 10px 0 0; font-size: 14px; background: ${theme.buttonPrimary}; }
        `;
        
        if (!document.getElementById('trixbox-changelog-style')) {
            const styleSheet = document.createElement("style");
            styleSheet.id = 'trixbox-changelog-style';
            styleSheet.innerText = modalStyle;
            document.head.appendChild(styleSheet);
        }
        
        const modalOverlay = document.createElement('div');
        modalOverlay.className = 'trixbox-changelog-overlay';
        modalOverlay.innerHTML = `
            <div class="trixbox-changelog-content">
                ${changelog}
                <button class="trixbox-changelog-btn">Close</button>
            </div>
        `;
        document.body.appendChild(modalOverlay);
        
        modalOverlay.querySelector('.trixbox-changelog-btn').onclick = () => { modalOverlay.remove(); };
        modalOverlay.onclick = (e) => {
            if (e.target === modalOverlay) modalOverlay.remove();
        };
    };

    // --- 3. CREATE THE TOGGLE ICON ---
    const toggleIcon = document.createElement('div');
    toggleIcon.id = 'trixbox-toggle-icon';
    toggleIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="${theme.text}" width="28px" height="28px"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></svg>`;
    // **FIX IS HERE**: Changed z-index to zIndex
    Object.assign(toggleIcon.style, {
        backgroundColor: theme.icon,
        position: 'fixed', bottom: '20px', right: '20px', width: '50px', height: '50px',
        borderRadius: '50%', display: 'flex', alignItems: 'center',
        justifyContent: 'center', cursor: 'pointer', zIndex: '99998',
        boxShadow: '0 2px 8px rgba(0,0,0,0.3)', transition: 'transform 0.2s ease-in-out'
    });
    toggleIcon.onmouseover = () => { toggleIcon.style.transform = 'scale(1.1)'; };
    toggleIcon.onmouseout = () => { toggleIcon.style.transform = 'scale(1.0)'; };
    document.body.appendChild(toggleIcon);
 
    // --- 3. CREATE THE CHATBOX AND LINK BLOCKERS ---
    const chatContainer = document.createElement('div');
    chatContainer.id = 'trixbox-container';
    Object.assign(chatContainer.style, {
        position: 'fixed', bottom: '15px', right: '15px', width: '350px', height: '500px',
        zIndex: '99999', display: 'none', flexDirection: 'column',
        boxShadow: '0 4px 12px rgba(0,0,0,0.3)', borderRadius: '8px', overflow: 'hidden'
    });
 
    const dragHeader = document.createElement('div');
    dragHeader.id = 'trixbox-header';
    Object.assign(dragHeader.style, {
        height: '30px', backgroundColor: 'rgb(210, 210, 255)', cursor: 'move',
        userSelect: 'none', display: 'flex', alignItems: 'center',
        justifyContent: 'space-between', padding: '0 5px 0 15px'
    });
 
    const headerTitle = document.createElement('span');
    headerTitle.textContent = 'TrixBox Chat';
    Object.assign(headerTitle.style, {
        color: 'white', fontWeight: 'bold', fontSize: '14px',
        textShadow: '1px 1px 0 rgb(160, 160, 230), 2px 2px 0 rgba(0, 0, 0, 0.15)'
    });
    dragHeader.appendChild(headerTitle);

    const closeButton = document.createElement('button');
    closeButton.innerHTML = '&times;';
    Object.assign(closeButton.style, {
        background: 'none', border: 'none', color: 'rgb(80, 80, 120)',
        fontSize: '24px', lineHeight: '1', cursor: 'pointer', padding: '0 8px'
    });
    dragHeader.appendChild(closeButton);

    // Settings/Themes button
    const settingsBtn = document.createElement('button');
    settingsBtn.innerHTML = '⚙️';
    Object.assign(settingsBtn.style, {
        background: 'none', border: 'none', color: 'rgb(80, 80, 120)',
        fontSize: '18px', lineHeight: '1', cursor: 'pointer', padding: '0 8px', marginRight: '5px'
    });
    dragHeader.insertBefore(settingsBtn, closeButton);

    // Changelog button
    const changelogBtn = document.createElement('button');
    changelogBtn.innerHTML = '📋';
    Object.assign(changelogBtn.style, {
        background: 'none', border: 'none', color: 'rgb(80, 80, 120)',
        fontSize: '18px', lineHeight: '1', cursor: 'pointer', padding: '0 8px', marginRight: '5px'
    });
    dragHeader.insertBefore(changelogBtn, settingsBtn);
    chatContainer.appendChild(dragHeader);

    const iframeWrapper = document.createElement('div');
    Object.assign(iframeWrapper.style, { position: 'relative', flexGrow: '1' });
    chatContainer.appendChild(iframeWrapper);

    // --- 3A. ADVANCED SETTINGS MODAL ---
    const showAdvancedSettings = () => {
        const settingsStyle = `
            .trixbox-settings-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 100001; display: flex; align-items: center; justify-content: center; }
            .trixbox-settings-content { background: ${theme.modalBg}; color: ${theme.text}; padding: 25px; border-radius: 10px; text-align: left; font-family: sans-serif; box-shadow: 0 5px 15px rgba(0,0,0,0.5); max-width: 500px; max-height: 700px; overflow-y: auto; }
            .trixbox-settings-content h2 { margin-top: 0; color: ${theme.buttonPrimary}; text-align: center; }
            .trixbox-settings-section { margin: 20px 0; padding: 15px; background: ${theme.buttonSecondary}; border-radius: 5px; }
            .trixbox-settings-section h3 { margin: 0 0 10px 0; color: ${theme.buttonPrimary}; font-size: 14px; }
            .trixbox-settings-label { display: block; margin: 10px 0 5px 0; font-size: 13px; color: ${theme.text}; }
            .trixbox-settings-input, .trixbox-settings-select, .trixbox-settings-textarea { width: 100%; padding: 8px; margin: 5px 0 10px 0; background: #36393f; color: ${theme.text}; border: 1px solid #202225; border-radius: 4px; font-family: monospace; font-size: 12px; box-sizing: border-box; }
            .trixbox-settings-textarea { min-height: 150px; resize: vertical; }
            .trixbox-settings-btn { color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; margin: 5px 5px 5px 0; font-size: 13px; }
            .trixbox-settings-btn.primary { background: ${theme.buttonPrimary}; }
            .trixbox-settings-btn.secondary { background: #5865f2; }
            .trixbox-settings-btn.danger { background: #f04747; }
            .trixbox-settings-btn:hover { opacity: 0.9; }
            .trixbox-sound-preview { padding: 10px; background: #2f3136; border-radius: 4px; margin: 10px 0; }
            .trixbox-sound-preview button { padding: 8px 12px; font-size: 12px; }
        `;
        
        if (!document.getElementById('trixbox-settings-style')) {
            const styleSheet = document.createElement("style");
            styleSheet.id = 'trixbox-settings-style';
            styleSheet.innerText = settingsStyle;
            document.head.appendChild(styleSheet);
        }
        
        const modalOverlay = document.createElement('div');
        modalOverlay.className = 'trixbox-settings-overlay';
        
        const savedStyles = localStorage.getItem('trixbox-custom-styles') || customStyleManager.getDefaultStyles();
        
        modalOverlay.innerHTML = `
            <div class="trixbox-settings-content">
                <h2>⚙️ Advanced Settings</h2>
                
                <div class="trixbox-settings-section">
                    <h3>🔊 Notification Sound</h3>
                    <label class="trixbox-settings-label">Sound Type:</label>
                    <select class="trixbox-settings-select" id="notification-sound-select">
                        <option value="default" ${notificationSound === 'default' ? 'selected' : ''}>Default</option>
                        <option value="none" ${notificationSound === 'none' ? 'selected' : ''}>Disabled</option>
                        <option value="custom">Custom URL</option>
                    </select>
                    <div class="trixbox-sound-preview">
                        <button class="trixbox-settings-btn secondary" id="test-sound-btn">🔊 Test Sound</button>
                    </div>
                    <label class="trixbox-settings-label">Custom Sound URL (if selected above):</label>
                    <input type="text" class="trixbox-settings-input" id="custom-sound-url" placeholder="https://example.com/sound.mp3" value="${localStorage.getItem('trixbox-custom-sound-url') || ''}">
                </div>
                
                <div class="trixbox-settings-section">
                    <h3>🎨 Custom CSS Styles</h3>
                    <p style="font-size: 12px; color: #72767d; margin: 10px 0;">Edit the CSS below to customize chat appearance. Leave blank to use theme defaults.</p>
                    <textarea class="trixbox-settings-textarea" id="custom-css-input" placeholder="Enter custom CSS here...">${savedStyles}</textarea>
                    <button class="trixbox-settings-btn primary" id="apply-css-btn">✅ Apply Styles</button>
                    <button class="trixbox-settings-btn secondary" id="reset-css-btn">↻ Reset to Default</button>
                    <button class="trixbox-settings-btn secondary" id="load-default-btn">📋 Load Default CSS</button>
                </div>
                
                <div class="trixbox-settings-section">
                    <h3>ℹ️ Settings Info</h3>
                    <p style="font-size: 12px; color: #72767d; margin: 0;">
                        Current Theme: <strong>${currentTheme}</strong><br>
                        Custom Styles: <strong>${customCSSEnabled ? 'Enabled' : 'Disabled'}</strong><br>
                        Notification: <strong>${notificationSound === 'default' ? 'Default' : notificationSound === 'none' ? 'Disabled' : 'Custom'}</strong>
                    </p>
                </div>
                
                <div style="text-align: center;">
                    <button class="trixbox-settings-btn primary" id="close-settings-btn">Close Settings</button>
                </div>
            </div>
        `;
        document.body.appendChild(modalOverlay);
        
        // Event listeners
        document.getElementById('notification-sound-select').addEventListener('change', (e) => {
            notificationSound = e.target.value;
            localStorage.setItem('trixbox-notification-sound', notificationSound);
        });
        
        document.getElementById('test-sound-btn').addEventListener('click', () => {
            const audio = new Audio();
            if (notificationSound === 'custom') {
                audio.src = document.getElementById('custom-sound-url').value;
            } else if (notificationSound !== 'none') {
                audio.src = 'data:audio/wav;base64,UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAAAAA==';
            }
            if (audio.src) audio.play().catch(() => {});
        });
        
        document.getElementById('apply-css-btn').addEventListener('click', () => {
            const css = document.getElementById('custom-css-input').value;
            customStyleManager.applyCustomStyles(css);
            customCSSEnabled = true;
            localStorage.setItem('trixbox-custom-css', 'true');
            alert('✅ Custom styles applied!');
        });
        
        document.getElementById('reset-css-btn').addEventListener('click', () => {
            customStyleManager.resetStyles();
            document.getElementById('custom-css-input').value = customStyleManager.getDefaultStyles();
            alert('✅ Styles reset to default theme!');
        });
        
        document.getElementById('load-default-btn').addEventListener('click', () => {
            document.getElementById('custom-css-input').value = customStyleManager.getDefaultStyles();
        });
        
        document.getElementById('custom-sound-url').addEventListener('change', (e) => {
            localStorage.setItem('trixbox-custom-sound-url', e.target.value);
        });
        
        document.getElementById('close-settings-btn').addEventListener('click', () => {
            modalOverlay.remove();
        });
        
        modalOverlay.addEventListener('click', (e) => {
            if (e.target === modalOverlay) modalOverlay.remove();
        });
    };

    // --- 3A. THEME SELECTOR MODAL ---
    const showThemeSelector = () => {
        const themeSelectorStyle = `
            .trixbox-theme-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 100001; display: flex; align-items: center; justify-content: center; }
            .trixbox-theme-content { background: ${theme.modalBg}; color: ${theme.text}; padding: 25px; border-radius: 10px; text-align: center; font-family: sans-serif; box-shadow: 0 5px 15px rgba(0,0,0,0.5); max-width: 400px; max-height: 500px; overflow-y: auto; }
            .trixbox-theme-content h2 { margin-top: 0; color: ${theme.buttonPrimary}; }
            .trixbox-theme-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 20px 0; }
            .trixbox-theme-option { padding: 12px; background: ${theme.buttonSecondary}; border: 2px solid transparent; border-radius: 5px; cursor: pointer; transition: all 0.2s; color: ${theme.text}; }
            .trixbox-theme-option:hover { background: ${theme.buttonPrimary}; }
            .trixbox-theme-option.active { border-color: ${theme.buttonPrimary}; background: ${theme.buttonPrimary}; }
            .trixbox-theme-btn { color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 10px 5px 0 0; font-size: 14px; background: ${theme.buttonPrimary}; }
        `;
        
        if (!document.getElementById('trixbox-theme-style')) {
            const styleSheet = document.createElement("style");
            styleSheet.id = 'trixbox-theme-style';
            styleSheet.innerText = themeSelectorStyle;
            document.head.appendChild(styleSheet);
        }
        
        const modalOverlay = document.createElement('div');
        modalOverlay.className = 'trixbox-theme-overlay';
        
        let themeOptionsHTML = '<div class="trixbox-theme-grid">';
        themes.forEach(t => {
            const isActive = t === currentTheme ? 'active' : '';
            themeOptionsHTML += `<div class="trixbox-theme-option ${isActive}" data-theme="${t}">${t}</div>`;
        });
        themeOptionsHTML += '</div>';
        
        modalOverlay.innerHTML = `
            <div class="trixbox-theme-content">
                <h2>Choose Theme</h2>
                ${themeOptionsHTML}
                <button class="trixbox-theme-btn close-theme-btn">Close</button>
            </div>
        `;
        document.body.appendChild(modalOverlay);
        
        modalOverlay.querySelectorAll('.trixbox-theme-option').forEach(option => {
            option.addEventListener('click', () => {
                const themeName = option.getAttribute('data-theme');
                modalOverlay.querySelectorAll('.trixbox-theme-option').forEach(o => o.classList.remove('active'));
                option.classList.add('active');
                applyTheme(themeName);
            });
        });
        
        modalOverlay.querySelector('.close-theme-btn').onclick = () => { modalOverlay.remove(); };
        modalOverlay.onclick = (e) => {
            if (e.target === modalOverlay) modalOverlay.remove();
        };
    };

    // Create the chat library script
    const chatLibraryScript = document.createElement('script');
    chatLibraryScript.src = 'https://iframe.chat/scripts/main.min.js';
    document.head.appendChild(chatLibraryScript);

    // Create the iframe FIRST (before initialization)
    const chatIframe = document.createElement('iframe');
    chatIframe.src = 'https://iframe.chat/embed?chat=15234533';
    chatIframe.id = 'chattable';
    Object.assign(chatIframe.style, {
        width: '100%', height: '100%', border: 'none',
        backgroundColor: 'transparent'
    });
    iframeWrapper.appendChild(chatIframe);
    document.body.appendChild(chatContainer);

    // Initialize Chattable after iframe and library are ready
    const initializeChattable = () => {
        if (window.chattable && window.chattable.initialize && typeof window.chattable.initialize === 'function') {
            const initConfig = {
                theme: currentTheme.toLowerCase().replace(/\s+/g, '')
            };
            
            // Add custom stylesheet if CSS is enabled
            const customStyles = localStorage.getItem('trixbox-custom-styles');
            if (customCSSEnabled && customStyles) {
                initConfig.stylesheet = customStyles;
            }
            
            window.chattable.initialize(initConfig);
            console.log('TrixBox: Chattable initialized with config:', initConfig);
        } else {
            // Retry if library not ready
            if (!window.chattableInitRetries) {
                window.chattableInitRetries = 0;
            }
            window.chattableInitRetries++;
            
            if (window.chattableInitRetries < 50) {
                setTimeout(initializeChattable, 100);
            } else {
                console.error('TrixBox: Failed to initialize Chattable after 50 attempts');
            }
        }
    };

    // Wait for DOM to be fully loaded before initializing
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(initializeChattable, 1000);
        });
    } else {
        setTimeout(initializeChattable, 1000);
    }
 
    const headerLinkBlocker = document.createElement('div');
    Object.assign(headerLinkBlocker.style, { position: 'absolute', top: '0px', left: '0px', width: '100px', height: '45px', zIndex: '2' });
    iframeWrapper.appendChild(headerLinkBlocker);
 
    const settingsLinkBlocker = document.createElement('div');
    Object.assign(settingsLinkBlocker.style, {
        position: 'absolute', bottom: '12px', left: '50%',
        transform: 'translateX(-50%)',
        width: '140px',
        height: '25px',
        zIndex: '2'
    });
    iframeWrapper.appendChild(settingsLinkBlocker);
 
    // --- 4. ADD FUNCTIONALITY (TOGGLE, DRAG, ETC.) ---
    toggleIcon.addEventListener('click', () => { chatContainer.style.display = 'flex'; toggleIcon.style.display = 'none'; });
 
    closeButton.addEventListener('click', (e) => {
        e.stopPropagation();
        chatContainer.style.display = 'none';
        toggleIcon.style.display = 'flex';
    });

    settingsBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        showAdvancedSettings();
    });

    changelogBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        showChangelogModal();
    });
 
    let isDragging = false, offsetX, offsetY;
    dragHeader.addEventListener('mousedown', (e) => {
        if (e.target !== dragHeader && e.target !== headerTitle) return;
        isDragging = true;
        offsetX = e.clientX - chatContainer.getBoundingClientRect().left;
        offsetY = e.clientY - chatContainer.getBoundingClientRect().top;
        chatIframe.style.pointerEvents = 'none';
    });
 
    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        chatContainer.style.left = `${e.clientX - offsetX}px`;
        chatContainer.style.top = `${e.clientY - offsetY}px`;
        chatContainer.style.bottom = 'auto';
        chatContainer.style.right = 'auto';
    });
 
    document.addEventListener('mouseup', () => {
        if (!isDragging) return;
        isDragging = false;
        chatIframe.style.pointerEvents = 'auto';
    });

    // Show changelog on first load or when version changes
    const lastSeenVersion = localStorage.getItem('trixbox-version');
    if (lastSeenVersion !== '0.3.2') {
        setTimeout(() => showChangelogModal(), 2000);
        localStorage.setItem('trixbox-version', '0.3.2');
    }
 
    // --- 5. RUN THE UPDATE CHECKER ---
    checkForUpdates();
})();