ImmediateGUI

An IMGUI inspired GUI Framework for javascript thats designed to be as simple to use as IMGUI.

当前为 2025-05-17 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/535798/1590637/ImmediateGUI.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

class ImmediateGUI {
    static OccupiedElementIds = [];
    static GenerateId(prefix = "gui_") {
        if (typeof prefix !== 'string' || prefix.length === 0) {
            prefix = "gui_";
        }
        const timestamp = Date.now().toString(36);
        const randomPart = Math.random().toString(36).substring(2, 15);
        const generatedId = prefix + timestamp + randomPart;
        const exists = ImmediateGUI.OccupiedElementIds.includes(generatedId);
        if (exists) return ImmediateGUI.GenerateId(prefix);
        ImmediateGUI.OccupiedElementIds.push(generatedId);
        return generatedId;
    }

    constructor(options = {}) {
        this.options = {
            theme: 'light',
            position: 'right',
            width: 300,
            draggable: true,
            ...options
        };

        this.themes = {
            light: {
                background: '#ffffff',
                text: '#333333',
                border: '#cccccc',
                accent: '#4285f4',
                buttonBg: '#f5f5f5',
                buttonHover: '#e0e0e0',
                inputBg: '#ffffff',
                sectionBg: '#f9f9f9'
            },
            dark: {
                background: '#2d2d2d',
                text: '#e0e0e0',
                border: '#555555',
                accent: '#4d90fe',
                buttonBg: '#444444',
                buttonHover: '#555555',
                inputBg: '#3d3d3d',
                sectionBg: '#333333'
            }
        };

        this.maxHeight = '85vh';

        this.theme = this.themes[this.options.theme] || this.themes.light;

        // Create main container
        this.container = document.createElement('div');
        this.container.id = ImmediateGUI.GenerateId();
        this.container.style.cssText = `
            position: fixed;
            ${this.options.position === 'right' ? 'right' : 'left'}: 10px;
            top: 10px;
            width: ${this.options.width}px;
            background: ${this.theme.background};
            color: ${this.theme.text};
            border: 1px solid ${this.theme.border};
            border-radius: 4px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            font-size: 14px;
            z-index: 9999;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            padding: 12px;
            max-height: ${this.maxHeight};
            overflow-y: auto;
            overflow-x: hidden;
            transition: all 0.2s ease;
        `;

        this._applyGlobalStyles();

        this.currentSection = null;

        this.indentationLevel = 0;
        this.indentationSize = 10; // pixels per level
        this.isCustomIndentationLevel = false;


        if (this.options.draggable) {
            this._setupDragging();
        }
    }

    _applyGlobalStyles() {
        if (!document.getElementById('imgui-global-styles')) {
            const styleEl = document.createElement('style');
            styleEl.id = 'imgui-global-styles';
            styleEl.textContent = `
                .imgui-control {
                    margin-bottom: 2px;
                    width: 100%;
                    box-sizing: border-box;
                }
                
                .imgui-button {
                    background: ${this.theme.buttonBg};
                    color: ${this.theme.text};
                    border: 1px solid ${this.theme.border};
                    border-radius: 4px;
                    padding: 8px 12px;
                    font-size: 14px;
                    cursor: pointer;
                    transition: all 0.2s ease;
                    outline: none;
                    width: auto;
                    font-family: inherit;
                    margin-right: 5px;
                }
                
                .imgui-button:hover {
                    background: ${this.theme.buttonHover};
                }
                
                .imgui-button:active {
                    transform: translateY(1px);
                }
                
                .imgui-input {
                    background: ${this.theme.inputBg};
                    color: ${this.theme.text};
                    border: 1px solid ${this.theme.border};
                    border-radius: 4px;
                    padding: 8px 10px;
                    font-size: 14px;
                    width: 100%;
                    box-sizing: border-box;
                    outline: none;
                    transition: border-color 0.2s ease;
                    font-family: inherit;
                }
                
                .imgui-input:focus {
                    border-color: ${this.theme.accent};
                }
                
                .imgui-section {
                    border: 1px solid ${this.theme.border};
                    border-radius: 4px;
                    padding: 10px;
                    margin-bottom: 12px;
                    background: ${this.theme.sectionBg};
                }
                
                .imgui-section-header {
                    font-weight: 600;
                    margin-bottom: 8px;
                    padding-bottom: 6px;
                    border-bottom: 1px solid ${this.theme.border};
                    color: ${this.theme.text};
                }
                
                .imgui-label {
                    display: block;
                    margin-bottom: 4px;
                    color: ${this.theme.text};
                    font-weight: 500;
                }
            `;
            document.head.appendChild(styleEl);
        }
    }

    _setupDragging() {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        const isClickOnControl = (element) => {
            if (!element) return false;
            
            let current = element;
            while (current && current !== this.container) {
                if (
                    current.classList.contains('imgui-control') ||
                    current.tagName === 'INPUT' ||
                    current.tagName === 'BUTTON' ||
                    current.tagName === 'SELECT' ||
                    current.tagName === 'TEXTAREA'
                ) {
                    return true;
                }
                current = current.parentElement;
            }
            return false;
        };

        this.container.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return; 
            if (isClickOnControl(e.target)) {
                return;
            }
            
            isDragging = true;
            
            startX = e.clientX;
            startY = e.clientY;
            
            const rect = this.container.getBoundingClientRect();
            startLeft = rect.left;
            startTop = rect.top;
            
            document.body.style.cursor = 'move';
            
            e.preventDefault();
        });
        
        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            
            const newLeft = startLeft + dx;
            const newTop = startTop + dy;
            
            this.container.style.left = `${newLeft}px`;
            this.container.style.top = `${newTop}px`;
            this.container.style.right = 'auto';
        });
        
        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                document.body.style.cursor = '';
                
                this._keepInView();
            }
        });
        
        document.addEventListener('mouseleave', () => {
            if (isDragging) {
                isDragging = false;
                document.body.style.cursor = '';
            }
        });
    }
    
    _keepInView() {
        const rect = this.container.getBoundingClientRect();
        const windowWidth = window.innerWidth;
        const windowHeight = window.innerHeight;
        
        const minVisiblePx = 50;
        
        let newLeft = rect.left;
        let newTop = rect.top;
        
        if (rect.right < minVisiblePx) {
            newLeft = minVisiblePx - rect.width;
        } else if (rect.left > windowWidth - minVisiblePx) {
            newLeft = windowWidth - minVisiblePx;
        }
        
        if (rect.bottom < minVisiblePx) {
            newTop = minVisiblePx - rect.height;
        } else if (rect.top > windowHeight - minVisiblePx) {
            newTop = windowHeight - minVisiblePx;
        }
        
        if (newLeft !== rect.left || newTop !== rect.top) {
            this.container.style.left = `${newLeft}px`;
            this.container.style.top = `${newTop}px`;
        }
    }

    // Section management
    BeginSection(title, collapsible = false, collapsedByDefault = false) {
        const section = document.createElement('div');
        section.className = 'imgui-section';
        section.id = ImmediateGUI.GenerateId('section_');
        
        if (title) {
            const header = document.createElement('div');
            header.className = 'imgui-section-header';
            
            if (collapsible) {
                header.style.cssText = `
                    display: flex;
                    align-items: center;
                    cursor: pointer;
                    user-select: none;
                    margin-bottom: 8px;
                    padding-bottom: 6px;
                    border-bottom: 1px solid ${this.theme.border};
                    color: ${this.theme.text};
                `;
                
                const indicator = document.createElement('span');
                indicator.className = 'imgui-section-indicator';
                indicator.textContent = '▼';
                indicator.style.cssText = `
                    margin-right: 8px;
                    font-size: 10px;
                    transition: transform 0.2s ease;
                `;
                
                const titleSpan = document.createElement('span');
                titleSpan.textContent = title;
                titleSpan.style.flex = '1';
                
                const content = document.createElement('div');
                content.className = 'imgui-section-content';
                content.style.cssText = `
                    overflow: hidden;
                    transition: max-height 0.3s ease;
                `;
                
                section.isCollapsed = false;
                
                const toggleCollapse = () => {
                    section.isCollapsed = !section.isCollapsed;
                    
                    if (section.isCollapsed) {
                        content.style.maxHeight = '0px';
                        indicator.textContent = '►';
                        // TODO: figure out why the ► character looks squished and skinny??
                        //indicator.style.fontSize = '14px';
                        indicator.style.transform = 'rotate(0deg)';
                    } else {
                        content.style.maxHeight = '2000px';
                        indicator.textContent = '▼'; 
                        indicator.style.transform = 'rotate(0deg)';
                    }
                };

                if (collapsedByDefault) toggleCollapse();
                
                header.addEventListener('click', toggleCollapse);
                
                header.appendChild(indicator);
                header.appendChild(titleSpan);
                section.appendChild(header);
                section.appendChild(content);
                
                section.contentContainer = content;
            } else {
                header.textContent = title;
                section.appendChild(header);
            }
        }
        
        this.container.appendChild(section);
        this.currentSection = section;
        return this;
    }

    EndSection() {
        this.currentSection = null;
        return this;
    }

    // Indentation management
    BeginIndentation(level = -1) {
        if (level === -1) this.indentationLevel++;
        else {
            this.isCustomIndentationLevel = true;
            this.indentationLevel = level;
        }
        return this;
    }

    EndIndentation() {
        if (this.indentationLevel > 0) {
            if (this.isCustomIndentationLevel) {
                this.indentationLevel = 0;
                this.isCustomIndentationLevel = false;
            }
            else this.indentationLevel--;
        }
        return this;
    }

    _applyIndentation(element) {
        if (this.indentationLevel > 0) {
            const currentIndent = this.indentationLevel * this.indentationSize;
            element.style.marginLeft = `${currentIndent}px`;
        }
        return element;
    }

    // Utility to get current target container
    _getTargetContainer() {
        if (this.currentSection) {
            // If current section is collapsible, use its content container
            if (this.currentSection.contentContainer) {
                return this.currentSection.contentContainer;
            }
            return this.currentSection;
        }
        return this.container;
    }

    // Original API methods with improved implementation
    GetControlContainer() {
        return this.container;
    }

    GetControls() {
        return this.container.querySelectorAll('.imgui-control');
    }

    Separator() {
        const separator = document.createElement('hr');
        separator.id = ImmediateGUI.GenerateId("ctrl_");
        separator.style.cssText = `
            border: none;
            border-top: 1px solid ${this.theme.border};
            margin: 10px 0;
            width: 100%;
        `;
        
        this._applyIndentation(separator);
        this._getTargetContainer().appendChild(separator);
        return separator;
    }

    Header(text, level = 1) {
        const validLevel = Math.min(Math.max(level, 1), 6);
        const header = document.createElement(`h${validLevel}`);
        header.id = ImmediateGUI.GenerateId("ctrl_");
        header.textContent = text;
        header.style.cssText = `
            margin: 0 0 10px 0;
            padding: 0;
            font-weight: ${validLevel <= 2 ? 'bold' : '600'};
            color: ${this.theme.text};
            font-size: ${24 - (validLevel * 2)}px;
            font-family: inherit;
        `;
        this._applyIndentation(header);
        this._getTargetContainer().appendChild(header);
        return header;
    }

    LabelControlPair(text, controlGeneratorFunction) {
        const wrapper = document.createElement("div");
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: 10px;
        `;

        const label = document.createElement('label');
        label.id = ImmediateGUI.GenerateId("ctrl_");
        label.textContent = text;
        label.className = "imgui-label";
        
        const control = typeof controlGeneratorFunction === "function" 
            ? controlGeneratorFunction() 
            : controlGeneratorFunction;
        
        control.label = label;

        wrapper.appendChild(label);
        wrapper.appendChild(control);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return control;
    }

    Button(text, callback) {
        const btn = document.createElement('button');
        btn.id = ImmediateGUI.GenerateId("ctrl_");
        btn.textContent = text;
        btn.className = "imgui-button imgui-control";
        
        if (callback && typeof callback === 'function') {
            btn.addEventListener('click', callback);
        }
        
        this._applyIndentation(btn);
        this._getTargetContainer().appendChild(btn);
        return btn;
    }

    Textbox(placeholder, defaultValue = "") {
        const input = document.createElement('input');
        input.id = ImmediateGUI.GenerateId("ctrl_");
        input.type = 'text';
        input.placeholder = placeholder;
        input.value = defaultValue;
        input.className = "imgui-input imgui-control";
        
        this._applyIndentation(input);
        this._getTargetContainer().appendChild(input);
        return input;
    }

    TextArea(placeholder = "", defaultValue = "", rows = 4) {
        const textarea = document.createElement('textarea');
        textarea.id = ImmediateGUI.GenerateId("ctrl_");
        textarea.placeholder = placeholder;
        textarea.value = defaultValue;
        textarea.rows = rows;
        textarea.className = "imgui-input imgui-control";
        textarea.style.cssText = `
            resize: vertical;
            min-height: ${rows * 20}px;
            font-family: inherit;
            margin-bottom: 10px;
            max-height: calc(${this.maxHeight} - 50px); /* Limit max height to prevent overflowing */
        `;
        
        textarea.addEventListener('mouseup', () => {
            const container = this.container;
            const containerMaxHeight = parseInt(getComputedStyle(container).maxHeight);
            const containerRect = container.getBoundingClientRect();
            const textareaRect = textarea.getBoundingClientRect();
            
            // Calculate how much space is available in the container
            const availableSpace = containerMaxHeight - (textareaRect.top - containerRect.top) - 20; // 20px buffer
            
            // If textarea is too tall, limit its height
            if (textarea.offsetHeight > availableSpace) {
                textarea.style.height = `${availableSpace}px`;
            }
        });

        this._applyIndentation(textarea);
        this._getTargetContainer().appendChild(textarea);
        return textarea;
    }

    Label(text) {
        const label = document.createElement('label');
        label.id = ImmediateGUI.GenerateId("ctrl_");
        label.textContent = text;
        label.className = "imgui-label imgui-control";
        
        this._applyIndentation(label);
        this._getTargetContainer().appendChild(label);
        return label;
    }

    ProgressBar(value = 0, min = 0, max = 100, showText = true) {
        // Create wrapper element
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            width: 100%;
            margin-bottom: 10px;
        `;
        
        // Create progress container
        const progressContainer = document.createElement('div');
        progressContainer.style.cssText = `
            width: 100%;
            height: 20px;
            background: ${this.theme.inputBg};
            border: 1px solid ${this.theme.border};
            border-radius: 4px;
            overflow: hidden;
            position: relative;
        `;
        
        // Create progress bar element
        const progressBar = document.createElement('div');
        progressBar.id = ImmediateGUI.GenerateId("ctrl_");
        progressBar.className = "imgui-progressbar imgui-control";
        
        // Calculate the percentage
        const normalizedValue = Math.min(Math.max(value, min), max);
        const percentage = ((normalizedValue - min) / (max - min)) * 100;
        
        progressBar.style.cssText = `
            width: ${percentage}%;
            height: 100%;
            background: ${this.theme.accent};
            transition: width 0.3s ease;
        `;
        
        // Optional text display
        let textElement = null;
        if (showText) {
            textElement = document.createElement('div');
            textElement.textContent = `${Math.round(percentage)}%`;
            textElement.style.cssText = `
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                color: ${this.theme.text};
                font-size: 12px;
                font-weight: bold;
                text-shadow: 0 0 2px rgba(0,0,0,0.5);
                pointer-events: none;
            `;
            progressContainer.appendChild(textElement);
        }
        
        // Add elements to the DOM
        progressContainer.appendChild(progressBar);
        wrapper.appendChild(progressContainer);
        
        // Add methods to update the progress bar
        progressBar.setValue = (newValue) => {
            const normalizedNewValue = Math.min(Math.max(newValue, min), max);
            const newPercentage = ((normalizedNewValue - min) / (max - min)) * 100;
            progressBar.style.width = `${newPercentage}%`;
            if (textElement) {
                textElement.textContent = `${Math.round(newPercentage)}%`;
            }
        };
        
        // Store references
        progressBar.textElement = textElement;
        progressBar.min = min;
        progressBar.max = max;
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return progressBar;
    }

    ColorPicker(defaultValue = '#000000') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `display: flex; align-items: center;`;
        
        const colorPicker = document.createElement('input');
        colorPicker.id = ImmediateGUI.GenerateId("ctrl_");
        colorPicker.type = 'color';
        colorPicker.value = defaultValue;
        colorPicker.style.cssText = `
            margin-right: 8px;
            width: 40px;
            height: 40px;
            border: 1px solid ${this.theme.border};
            border-radius: 4px;
            background: none;
            cursor: pointer;
        `;
        
        const colorValue = document.createElement('span');
        colorValue.textContent = defaultValue;
        colorValue.style.cssText = `
            font-family: monospace;
            color: ${this.theme.text};
        `;
        
        colorPicker.addEventListener('input', () => {
            colorValue.textContent = colorPicker.value;
        });
        
        wrapper.appendChild(colorPicker);
        wrapper.appendChild(colorValue);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return colorPicker;
    }

    DatePicker(defaultValue = new Date().toISOString().split('T')[0]) {
        const datePicker = document.createElement('input');
        datePicker.id = ImmediateGUI.GenerateId("ctrl_");
        datePicker.type = 'date';
        datePicker.value = defaultValue;
        datePicker.className = "imgui-input imgui-control";
        
        this._applyIndentation(datePicker);
        this._getTargetContainer().appendChild(datePicker);
        return datePicker;
    }

    Dropdown(options = [], defaultValue = null) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: 10px;
        `;
        
        const select = document.createElement('select');
        select.id = ImmediateGUI.GenerateId("ctrl_");
        select.className = "imgui-input imgui-dropdown imgui-control";
        select.style.cssText = `
            padding: 6px 10px;
            border: 1px solid ${this.theme.border};
            border-radius: 4px;
            background: ${this.theme.inputBg};
            color: ${this.theme.text};
            font-family: inherit;
            cursor: pointer;
            appearance: auto;
        `;
        
        // Add options to the select element
        options.forEach(option => {
            const optElement = document.createElement('option');
            
            // Handle both simple strings and {text, value} objects
            if (typeof option === 'object' && option !== null) {
                optElement.textContent = option.text || option.label || '';
                optElement.value = option.value !== undefined ? option.value : option.text || '';
            } else {
                optElement.textContent = option;
                optElement.value = option;
            }
            
            // Set as selected if it matches the default value
            if (defaultValue !== null && optElement.value === defaultValue) {
                optElement.selected = true;
            }
            
            select.appendChild(optElement);
        });
        
        wrapper.appendChild(select);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return select;
    }

    NumberInput(label, defaultValue = 0, min = null, max = null) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `display: flex; align-items: center;`;
        
        const labelElem = document.createElement('label');
        labelElem.textContent = label;
        labelElem.style.cssText = `
            margin-right: 10px;
            flex: 1;
            color: ${this.theme.text};
        `;
        
        const input = document.createElement('input');
        input.label = labelElem;
        input.id = ImmediateGUI.GenerateId("ctrl_");
        input.type = 'number';
        input.value = defaultValue;
        if (min !== null) input.min = min;
        if (max !== null) input.max = max;

        // TODO: hacky solution to make input elements respect .min and .max values when inputting values manually using the keyboard
        // if (min !== null || max !== null) {
        //     input.onkeyup = (e) => {
        //         const currentValue = parseInt(input.value);
        //         if (isNaN(currentValue)) {
        //             input.value = Math.floor(min);
        //             return;
        //         }
        //         // Clamp input.value to a value between input.min and input.max
        //         if (min !== null && currentValue < min) {
        //             input.value = Math.floor(min);
        //         } else if (max !== null && currentValue > max) {
        //             input.value = Math.floor(max);
        //         }
        //     };
        // }

        input.style.cssText = `
            width: 80px;
            padding: 6px;
            border: 1px solid ${this.theme.border};
            border-radius: 4px;
            background: ${this.theme.inputBg};
            color: ${this.theme.text};
            font-family: inherit;
        `;
        
        wrapper.appendChild(labelElem);
        wrapper.appendChild(input);
        
        //this._applyIndentation(wrapper);
        this._applyIndentation(labelElem);
        this._getTargetContainer().appendChild(wrapper);
        return input;
    }

    Slider(minValue = 0, maxValue = 100, defaultValue = 50) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `display: flex; flex-direction: column;`;
        
        const sliderContainer = document.createElement('div');
        sliderContainer.style.cssText = `display: flex; align-items: center; width: 100%;`;
        
        const slider = document.createElement('input');
        slider.id = ImmediateGUI.GenerateId("ctrl_");
        slider.type = 'range';
        slider.min = minValue;
        slider.max = maxValue;
        slider.value = defaultValue;
        slider.style.cssText = `
            flex: 1;
            margin-right: 8px;
            accent-color: ${this.theme.accent};
        `;
        
        const valueDisplay = document.createElement('span');
        valueDisplay.textContent = defaultValue;
        valueDisplay.style.cssText = `
            min-width: 40px;
            text-align: right;
            color: ${this.theme.text};
            font-family: inherit;
        `;
        
        slider.addEventListener('input', () => {
            valueDisplay.textContent = slider.value;
        });
        
        slider.label = valueDisplay;

        sliderContainer.appendChild(slider);
        sliderContainer.appendChild(valueDisplay);
        wrapper.appendChild(sliderContainer);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return slider;
    }

    Checkbox(label, checked = false) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control";
        wrapper.style.cssText = `display: flex; align-items: center;`;
        
        const checkbox = document.createElement('input');
        checkbox.id = ImmediateGUI.GenerateId("ctrl_");
        checkbox.type = 'checkbox';
        checkbox.checked = checked;
        checkbox.style.cssText = `
            margin-right: 8px;
            accent-color: ${this.theme.accent};
            width: 16px;
            height: 16px;
        `;
        
        const labelElem = document.createElement('label');
        labelElem.textContent = label;
        labelElem.htmlFor = checkbox.id;
        labelElem.style.cssText = `
            cursor: pointer;
            color: ${this.theme.text};
            font-family: inherit;
            margin-top: 6px;
        `;

        checkbox.label = labelElem;
        
        wrapper.appendChild(checkbox);
        wrapper.appendChild(labelElem);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return checkbox;
    }

    Show() {
        if (this.container.children.length === 0) return this;
        document.body.appendChild(this.container);
        return this;
    }

    Remove() {
        this.container.remove();
    }

    Hide() {
        this.container.style.display = "none";
        return this;
    }

    ShowModal(message, title = '', options = {}) {
        // Default options
        const config = {
            title: title || '',
            type: 'info', // 'info', 'warning', 'error'
            buttons: ['OK'],
            closeOnBackdropClick: true,
            width: 400,
            ...options
        };
        
        const backdrop = document.createElement('div');
        backdrop.className = 'imgui-modal-backdrop';
        backdrop.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.5);
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
            opacity: 0;
            transition: opacity 0.2s ease;
        `;
        
        const modal = document.createElement('div');
        modal.className = 'imgui-modal';
        modal.style.cssText = `
            background: ${this.theme.background};
            border: 1px solid ${this.theme.border};
            border-radius: 6px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
            width: ${config.width}px;
            max-width: 90vw;
            max-height: 80vh;
            overflow-y: auto;
            padding: 16px;
            transform: translateY(-20px);
            opacity: 0;
            transition: transform 0.3s ease, opacity 0.3s ease;
            font-family: inherit;
        `;
        
        if (config.title) {
            const title = document.createElement('div');
            title.className = 'imgui-modal-title';
            title.textContent = config.title;
            title.style.cssText = `
                font-size: 18px;
                font-weight: bold;
                color: ${this.theme.text};
                margin-bottom: 12px;
                padding-bottom: 8px;
                border-bottom: 1px solid ${this.theme.border};
            `;
            modal.appendChild(title);
        }
        
        let iconHtml = '';
        if (config.type === 'warning') {
            iconHtml = '<div style="color: #f0ad4e; margin-right: 10px; font-size: 24px;">⚠️</div>';
        } else if (config.type === 'error') {
            iconHtml = '<div style="color: #d9534f; margin-right: 10px; font-size: 24px;">❌</div>';
        } else if (config.type === 'info') {
            iconHtml = '<div style="color: #5bc0de; margin-right: 10px; font-size: 24px;">ℹ️</div>';
        } else if (config.type === 'success') {
            iconHtml = '<div style="color: #5cb85c; margin-right: 10px; font-size: 24px;">✅</div>';
        }
        
        const messageContainer = document.createElement('div');
        messageContainer.className = 'imgui-modal-message';
        messageContainer.style.cssText = `
            color: ${this.theme.text};
            margin-bottom: 16px;
            line-height: 1.5;
            display: flex;
            align-items: flex-start;
        `;
        
        if (iconHtml) {
            const iconElement = document.createElement('div');
            iconElement.innerHTML = iconHtml;
            messageContainer.appendChild(iconElement);
        }
        
        const messageText = document.createElement('div');
        messageText.style.flex = '1';
        
        if (typeof message === 'object' && message.nodeType) {
            messageText.appendChild(message);
        } else {
            messageText.textContent = message;
        }
        
        messageContainer.appendChild(messageText);
        modal.appendChild(messageContainer);
        
        const buttonsContainer = document.createElement('div');
        buttonsContainer.className = 'imgui-modal-buttons';
        buttonsContainer.style.cssText = `
            display: flex;
            justify-content: flex-end;
            gap: 8px;
            margin-top: 16px;
        `;
        
        const closeModal = () => {
            modal.style.transform = 'translateY(-20px)';
            modal.style.opacity = '0';
            backdrop.style.opacity = '0';
            
            setTimeout(() => {
                document.body.removeChild(backdrop);
            }, 300);
        };
        
        const buttonsList = Array.isArray(config.buttons) ? config.buttons : [config.buttons];
        
        buttonsList.forEach((buttonConfig) => {
            const isObject = typeof buttonConfig === 'object';
            const buttonText = isObject ? buttonConfig.text : buttonConfig;
            const isPrimary = isObject ? buttonConfig.primary : false;
            const callback = isObject ? buttonConfig.callback : null;
            
            const button = document.createElement('button');
            button.textContent = buttonText;
            button.className = isPrimary ? 'imgui-button imgui-primary-button' : 'imgui-button';
            
            if (isPrimary) {
                button.style.background = this.theme.accent;
                button.style.color = '#ffffff';
                button.style.borderColor = this.theme.accent;
            }
            
            button.addEventListener('click', () => {
                if (callback) callback();
                closeModal();
            });
            
            buttonsContainer.appendChild(button);
        });
        
        modal.appendChild(buttonsContainer);
        backdrop.appendChild(modal);
        
        if (config.closeOnBackdropClick) {
            backdrop.addEventListener('click', (e) => {
                if (e.target === backdrop) {
                    closeModal();
                }
            });
        }
        
        const escHandler = (e) => {
            if (e.key === 'Escape') {
                closeModal();
                document.removeEventListener('keydown', escHandler);
            }
        };
        document.addEventListener('keydown', escHandler);
    
        document.body.appendChild(backdrop);
        
        setTimeout(() => {
            backdrop.style.opacity = '1';
            modal.style.transform = 'translateY(0)';
            modal.style.opacity = '1';
        }, 10);
        
        return {
            close: closeModal,
            element: modal,
            backdrop: backdrop
        };
    }

    // New methods for better theming and layout
    SetTheme(themeName) {
        if (this.themes[themeName]) {
            this.options.theme = themeName;
            this.theme = this.themes[themeName];
            this._applyThemeToElements();
        }
        return this;
    }

    _applyThemeToElements() {
        // Update container
        this.container.style.background = this.theme.background;
        this.container.style.color = this.theme.text;
        this.container.style.borderColor = this.theme.border;
        
        // Update all controls
        this.container.querySelectorAll('.imgui-button').forEach(el => {
            el.style.background = this.theme.buttonBg;
            el.style.color = this.theme.text;
            el.style.borderColor = this.theme.border;
        });
        
        this.container.querySelectorAll('.imgui-input').forEach(el => {
            el.style.background = this.theme.inputBg;
            el.style.color = this.theme.text;
            el.style.borderColor = this.theme.border;
        });
        
        this.container.querySelectorAll('.imgui-section').forEach(el => {
            el.style.background = this.theme.sectionBg;
            el.style.borderColor = this.theme.border;
        });
        
        // Update text elements
        this.container.querySelectorAll('label, h1, h2, h3, h4, h5, h6, span').forEach(el => {
            el.style.color = this.theme.text;
        });
        
        return this;
    }
}