ImmediateGUI

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

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greasyfork.org/scripts/535798/1591785/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: 'dark',
            position: 'right',
            width: 300,
            draggable: true,
            title: 'Immediate GUI',
            titleLeftAligned: true,
            ...options
        };

        this.themes = {
            light: {
                background: '#ffffff',
                text: '#333333',
                border: '#cccccc',
                accent: '#4285f4',
                buttonBg: '#f5f5f5',
                buttonHover: '#e0e0e0',
                inputBg: '#ffffff',
                sectionBg: '#f9f9f9'
            },
            dark: {
                // background: '#2d2d2d',
                background: '#151617',
                // text: '#e0e0e0',
                text: '#eef0f2',
                // border: '#555555',
                border: '#425069',
                // accent: '#4d90fe',
                accent: '#294a7a',
                // buttonBg: '#444444',
                buttonBg: '#274972',
                // buttonHover: '#555555',
                buttonHover: '#336caf',
                // inputBg: '#3d3d3d',
                inputBg: '#20324d',
                // sectionBg: '#333333'
                sectionBg: '#232426',
            }
        };

        this.maxHeight = '85vh';
        //this.maxHeight = 'auto;';

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

        // Create main container
        this.container = document.createElement('div');
        this.container.id = ImmediateGUI.GenerateId();

        this._applyGlobalStyles();
        this._updateCSSVariables();

        this.container.style.cssText = `
            position: fixed;
            ${this.options.position === 'right' ? 'right' : 'left'}: 10px;
            top: 10px;
            min-width: ${(typeof this.options.width === 'string' ? this.options.width : this.options.width) + 'px'};
            background: var(--imgui-bg);
            color: var(--imgui-text);
            border: 1px solid var(--imgui-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: 0; /* Remove all padding */
            max-height: ${this.maxHeight};
            overflow-y: auto;
            overflow-x: hidden;
            transition: all 0.2s ease;
        `;

        this._createTitleBar(this.options.titleLeftAligned);
        
        this.contentContainer = document.createElement('div');
        this.contentContainer.id = ImmediateGUI.GenerateId('content_');

        this.contentContainer.style.cssText = `
            width: 100%;
            box-sizing: border-box;
            padding: 0 12px 12px 12px; /* Add padding to content area only */
        `;
        this.container.appendChild(this.contentContainer);

        this.currentSection = null;

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

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

    _createTitleBar(leftAlign = true) {
        this.titleBar = document.createElement('div');
        this.titleBar.className = 'imgui-titlebar';
        this.titleBar.style.cssText = `
            width: 100% !important;
            padding-top: var(--imgui-bottom-padding);
            background: var(--imgui-section-bg);
            color: var(--imgui-text);
            font-weight: bold;
            cursor: ${this.options.draggable ? 'pointer' : 'default'};
            user-select: none;
            box-sizing: border-box;
            position: sticky;
            top: 0;
            z-index: 999999999;
            margin-bottom: 12px;
            border-bottom: 1px solid var(--imgui-border);
            ${leftAlign ? 'text-align: left; padding-left: 12px;' : 'text-align: center;'}
            height: var(--imgui-title-height);
        `;

        this.titleBar.textContent = this.options.title || 'ImmediateGUI';
        this.container.appendChild(this.titleBar);
    }

    _updateCSSVariables() {
        document.documentElement.style.setProperty('--imgui-bg', this.theme.background);
        document.documentElement.style.setProperty('--imgui-text', this.theme.text);
        document.documentElement.style.setProperty('--imgui-border', this.theme.border);
        document.documentElement.style.setProperty('--imgui-accent', this.theme.accent);
        document.documentElement.style.setProperty('--imgui-button-bg', this.theme.buttonBg);
        document.documentElement.style.setProperty('--imgui-button-hover', this.theme.buttonHover);
        document.documentElement.style.setProperty('--imgui-input-bg', this.theme.inputBg);
        document.documentElement.style.setProperty('--imgui-section-bg', this.theme.sectionBg);
    }

    _applyGlobalStyles() {
        const styleSheetId = `imgui-global-styles_${this.container.id}`;

        if (!document.getElementById(styleSheetId)) {
            const styleEl = document.createElement('style');

            styleEl.id = styleSheetId;
            styleEl.textContent = `
                :root {
                    --imgui-bg: ${this.theme.background};
                    --imgui-text: ${this.theme.text};
                    --imgui-border: ${this.theme.border};
                    --imgui-accent: ${this.theme.accent};
                    --imgui-button-bg: ${this.theme.buttonBg};
                    --imgui-button-hover: ${this.theme.buttonHover};
                    --imgui-input-bg: ${this.theme.inputBg};
                    --imgui-section-bg: ${this.theme.sectionBg};
                    --imgui-bottom-padding: 3px;
                    --imgui-title-height: 27px;
                    --imgui-scrollbar-width: 0.5em;
                }

                #${this.container.id} {
                    scrollbar-gutter: auto;
                    /* scrollbar-color: var(--imgui-accent); */
                    /* scrollbar-width: thin; */

                    &::-webkit-scrollbar {
                        width: var(--imgui-scrollbar-width);
                        height: 0.5em;
                        background-color: var(--imgui-section-bg);
                    }

                    &::-webkit-scrollbar-thumb {
                        background-color: var(--imgui-accent);
                        /* border-radius: 3px; */
                    }

                    /* This positions the scrollbar to start below the title bar */
                    &::-webkit-scrollbar-button:start:decrement {
                        height: var(--imgui-title-height);
                        display: block;
                        background-color: transparent;
                    }
                }

                .imgui-titlebar {
                    width: 100% !important;
                    box-sizing: border-box;
                    right: 0;
                    left: 0;
                }

                .imgui-progressbar {

                }

                .imgui-slider {
                    accent-color: var(--imgui-accent);
                }

                .imgui-slider:hover {
                    accent-color: var(--imgui-button-hover);
                }

                .imgui-control {
                    margin-bottom: 2px;
                    width: 100%;
                    box-sizing: border-box;
                }
                
                .imgui-button {
                    background: var(--imgui-button-bg);
                    color: var(--imgui-text);
                    border: 1px solid var(--imgui-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: var(--imgui-button-hover);
                }
                
                .imgui-button:active {
                    transform: translateY(1px);
                }
                
                .imgui-input {
                    background: var(--imgui-input-bg);
                    color: var(--imgui-text);
                    border: 1px solid var(--imgui-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::placeholder {
                    color: var(--imgui-text);
                    opacity: 0.5;
                }
                
                .imgui-input:focus {
                    border-color: var(--imgui-accent);
                }
                
                .imgui-section {
                    border: 1px solid var(--imgui-border);
                    border-radius: 4px;
                    padding: 10px 10px 0px;
                    margin-bottom: 12px;
                    background: var(--imgui-section-bg);
                }
                
                .imgui-section-header {
                    font-weight: 600;
                    margin-bottom: 8px;
                    padding-bottom: 6px;
                    border-bottom: 1px solid var(--imgui-border);
                    color: var(--imgui-text);
                }
                
                .imgui-label {
                    display: block;
                    margin-bottom: 4px;
                    color: var(--imgui-text);
                    font-weight: 500;
                }
            `;
            document.head.appendChild(styleEl);
            this.container.classList.add('imgui-container');
        }
    }

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

        const isClickOnControl = (element) => {
            if (!element) return false;
            
            return !element.classList.contains('imgui-titlebar');

            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_');
        section.style.paddingBottom = 'var(--imgui-bottom-padding)';

        if (title) {
            const header = document.createElement('div');
            header.className = 'imgui-section-header';
            header.style.color = `var(--imgui-text)`;
            header.style.borderBottom = `1px solid var(--imgui-border)`;


            if (collapsible) {
                header.style.cssText = `
                    display: flex;
                    align-items: center;
                    cursor: pointer;
                    user-select: none;
                    margin-bottom: 8px;
                    padding-bottom: 6px;
                `;
                
                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._getTargetContainer().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;

        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.contentContainer; // Changed from this.container
    }

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

    GetControls() {
        return this.GetControlContainer().querySelectorAll('.imgui-wrapper');
    }

    Separator() {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            width: 100%;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        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%;
        `;
        
        wrapper.appendChild(separator);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return separator;
    }

    Header(text, level = 1) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            width: 100%;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        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;
            width: 100%;
        `;
        
        wrapper.appendChild(header);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return header;
    }

    Button(text, callback, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const btn = document.createElement('button');
        btn.id = ImmediateGUI.GenerateId("ctrl_");
        btn.textContent = text;
        btn.className = "imgui-button imgui-control";
        if (typeof tooltip === 'string' && tooltip.length > 0) btn.title = tooltip;
        
        btn.addEventListener('mouseenter', () => {
            //btn.style.background = this.theme.buttonHover;
            btn.style.background = 'var(--imgui-button-hover)';
        })
        btn.addEventListener('mouseout', () => {
            //btn.style.background = this.theme.buttonBg;
            btn.style.background = 'var(--imgui-button-bg)';
        })

        if (callback && typeof callback === 'function') {
            btn.addEventListener('click', callback);
        }
        
        wrapper.appendChild(btn);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return btn;
    }

    Textbox(placeholder, defaultValue = "", tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const input = document.createElement('input');
        input.id = ImmediateGUI.GenerateId("ctrl_");
        input.type = 'text';
        input.placeholder = placeholder;
        input.value = defaultValue;
        input.style.background = 'var(--imgui-input-bg)';
        input.style.border = '1px solid var(--imgui-border)';
        input.className = "imgui-input imgui-control";
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) input.title = tooltip;
        
        wrapper.appendChild(input);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return input;
    }

    TextArea(placeholder = "", defaultValue = "", rows = 4, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        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 = `
            background: var(--imgui-input-bg);
            resize: vertical;
            min-height: ${rows * 20}px;
            font-family: inherit;
            margin-bottom: 0;
            max-height: calc(${this.maxHeight} - 50px); /* Limit max height to prevent overflowing */
        `;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) textarea.title = tooltip;
        
        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`;
            }
        });

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

    Label(text) {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const label = document.createElement('label');
        label.id = ImmediateGUI.GenerateId("ctrl_");
        label.textContent = text;
        label.className = "imgui-label imgui-control";
        
        wrapper.appendChild(label);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return label;
    }

    ProgressBar(value = 0, min = 0, max = 100, showText = true, tooltip = '') {
        // Create wrapper element
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            width: 100%;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        // Create progress container
        const progressContainer = document.createElement('div');
        progressContainer.style.cssText = `
            width: 100%;
            height: 20px;
            background: var(--imgui-input-bg);
            border: 1px solid var(--imgui-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";
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) progressBar.title = tooltip;
        
        // 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: var(--imgui-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: var(--imgui-text);
                font-size: 12px;
                font-weight: 500;
                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', tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; align-items: center; padding-bottom: var(--imgui-bottom-padding);`;
        
        const colorPicker = document.createElement('input');
        colorPicker.id = ImmediateGUI.GenerateId("ctrl_");
        colorPicker.type = 'color';
        colorPicker.value = defaultValue;
        colorPicker.className = "imgui-input";
        colorPicker.style.cssText = `
            margin-right: 8px;
            width: 80px;
            height: 40px;
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            background: none;
            background-color: var(--imgui-input-bg);
            cursor: pointer;
        `;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) colorPicker.title = tooltip;
        
        const colorValue = document.createElement('span');
        colorValue.textContent = defaultValue;
        colorValue.style.cssText = `
            font-family: monospace;
            color: var(--imgui-text);
            cursor: pointer;
        `;

        //colorValue.addEventListener('click', () => { alert('lel'); });
        
        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], tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const datePicker = document.createElement('input');
        datePicker.id = ImmediateGUI.GenerateId("ctrl_");
        datePicker.type = 'date';
        datePicker.value = defaultValue;
        datePicker.className = "imgui-input imgui-control";
        datePicker.style.cursor = "pointer"; 
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) datePicker.title = tooltip;

        wrapper.appendChild(datePicker);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return datePicker;
    }

    Dropdown(options = [], defaultValue = null, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;

        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 var(--imgui-border);
            border-radius: 4px;
            background: var(--imgui-input-bg);
            color: var(--imgui-text);
            font-family: inherit;
            cursor: pointer;
            appearance: auto;
        `;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) select.title = tooltip;
        
        // Add options to the select element
        options.forEach(option => {
            const optElement = document.createElement('option');
            optElement.style.backgroundColor = 'var(--imgui-input-bg)';
            optElement.style.color = 'var(--imgui-text)';
            
            // 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, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; align-items: center; margin-bottom: var(--imgui-bottom-padding);`;
        
        const labelElem = document.createElement('label');
        labelElem.textContent = label;
        labelElem.style.cssText = `
            margin-right: 10px;
            margin-top:8px;
            flex: 1;
            color: var(--imgui-text);
        `;
        
        const input = document.createElement('input');
        input.label = labelElem;
        input.id = ImmediateGUI.GenerateId("ctrl_");
        input.type = 'number';
        input.value = defaultValue;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) input.title = tooltip;
        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.addEventListener('keyup', () => {
                let currentValue = parseInt(input.value);
                if (isNaN(currentValue)) {
                    input.value = Math.floor(min);
                }
                else {
                    if (min !== null && currentValue < min) {
                        input.value = Math.floor(min);
                    } else if (max !== null && currentValue > max) {
                        input.value = Math.floor(max);
                    }
                }
            });
        }

        input.className = 'imgui-input'
        input.style.cssText = `
            width: 80px;
            padding: 6px;
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            background: var(--imgui-input-bg);
            color: var(--imgui-text);
            font-family: inherit;
        `;
        
        wrapper.appendChild(labelElem);
        wrapper.appendChild(input);
        
        this._applyIndentation(labelElem);
        this._getTargetContainer().appendChild(wrapper);
        return input;
    }

    Slider(minValue = 0, maxValue = 100, defaultValue = 50, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; flex-direction: column; margin-bottom: var(--imgui-bottom-padding);`;
        
        const sliderContainer = document.createElement('div');
        sliderContainer.style.cssText = `display: flex; align-items: center; width: 100%;`;
        
        const slider = document.createElement('input');
        slider.className = "imgui-slider";
        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: var(--imgui-accent); */
        `;

        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) slider.title = tooltip;
        
        const valueDisplay = document.createElement('span');
        valueDisplay.textContent = defaultValue;
        valueDisplay.style.cssText = `
            min-width: 40px;
            text-align: right;
            color: var(--imgui-text);
            font-family: inherit;
            font-weight: 500;
        `;
        
        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, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; align-items: center; margin-bottom: var(--imgui-bottom-padding)`;
        
        const checkbox = document.createElement('input');
        checkbox.id = ImmediateGUI.GenerateId("ctrl_");
        checkbox.type = 'checkbox';
        checkbox.checked = checked;
        checkbox.style.cssText = `
            margin-right: 8px;
            accent-color: var(--imgui-accent);
            clip-path: circle(46% at 50% 50%);
            width: 16px;
            height: 16px;
        `;

        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) wrapper.title = tooltip;
        
        const labelElem = document.createElement('label');
        labelElem.textContent = label;
        labelElem.htmlFor = checkbox.id;
        labelElem.style.cssText = `
            cursor: pointer;
            color: var(--imgui-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;
    }

    RadioButtons(options = [], defaultValue = null, tooltip = '') {
        const wrapper = document.createElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) wrapper.title = tooltip;

        // Generate a unique group name for this set of radio buttons
        const groupName = ImmediateGUI.GenerateId("radio_group_");
        
        // Create an object to store references to the radio buttons
        const radioButtons = {};
        
        // Add radio buttons to the wrapper
        options.forEach(option => {
            // Handle both simple strings and {text, value} objects
            let text, value;
            if (typeof option === 'object' && option !== null) {
                text = option.text || option.label || '';
                value = option.value !== undefined ? option.value : option.text || '';
            } else {
                text = option;
                value = option;
            }
            
            const radioContainer = document.createElement('div');
            radioContainer.style.cssText = `
                display: flex;
                align-items: center;
            `;
            
            const radio = document.createElement('input');
            radio.id = ImmediateGUI.GenerateId("ctrl_");
            radio.type = 'radio';
            radio.name = groupName;
            radio.value = value;
            radio.checked = value === defaultValue;
            radio.style.cssText = `
                margin-right: 8px;
                accent-color: var(--imgui-accent);
                width: 16px;
                height: 16px;
            `;
            
            const label = document.createElement('label');
            label.textContent = text;
            label.htmlFor = radio.id;
            label.style.cssText = `
                cursor: pointer;
                color: var(--imgui-text);
                font-family: inherit;
                margin-top: ${radioContainer.style.marginBottom || '6px'};
            `;
            
            radio.label = label;
            
            radioContainer.appendChild(radio);
            radioContainer.appendChild(label);
            wrapper.appendChild(radioContainer);
            
            // Store reference to the radio button
            radioButtons[value] = radio;
        });
        
        // Add helper methods to the radio group
        radioButtons.getChecked = () => {
            const selected = wrapper.querySelector(`input[name="${groupName}"]:checked`);
            return selected ? selected.value : null;
        };
        
        radioButtons.setChecked = (value, checked) => {
            const radio = wrapper.querySelector(`input[name="${groupName}"][value="${value}"]`);
            if (radio) {
                radio.checked = checked;
            }
        };
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);
        return radioButtons;
    }

    Show() { 
        if (this.container.style.display === 'none') {
            this.container.style.display = 'block';
            return this;
        } else {
            if (this.indentationLevel > 0) {
                console.warn("ImmediateGUI: Show() called while in indentation mode. Did you forget to call EndIndentation() somewhere?");
            }

            if (this.currentSection) {
                console.warn("ImmediateGUI: Show() called while in section mode. Did you forget to call EndSection() somewhere?");
            }

            if (this.container.children.length === 0) return this;
            if (!document.body.contains(this.container)) {

                // Last control wrapper does not need bottom margin, strip that away
                this.GetControls()[this.GetControls().length - 1].style.marginBottom = '0px';
                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', 'success'
            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: var(--imgui-bg);
            border: 1px solid var(--imgui-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: var(--imgui-text);
                margin-bottom: 12px;
                padding-bottom: 8px;
                border-bottom: 1px solid var(--imgui-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: var(--imgui-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 = 'var(--imgui-accent)';
                button.style.color = '#ffffff';
                button.style.borderColor = 'var(--imgui-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._updateCSSVariables();
            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 => {
            // Shitty hack to make the section header text color change for sections
            // that are not collapsible
            el.querySelectorAll('.imgui-section-header').forEach(h => { h.style.color = this.theme.text;});
            el.style.background = this.theme.sectionBg;
            el.style.borderColor = this.theme.border;
        });

        // this.container.querySelectorAll('.imgui-progressbar').forEach(el => {
        //     // el.style.background = this.theme.inputBg;
        //     // 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;
    }
}