Greasy Fork

Greasy Fork is available in English.

ImmediateGUI

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

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

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

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