ImmediateGUI

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

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/535798/1780653/ImmediateGUI.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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);
        // FIXME: This could theoretically cause an infinite loop if we are extremely unlucky with random numbers
        if (exists) return ImmediateGUI._generateId(prefix);
        ImmediateGUI.OccupiedElementIds.push(generatedId);
        return generatedId;
    }

    static {
        this.HasStaticStyles = false;
        this.StaticStyleSheetId = `imgui-global-static-styles`;

        this._escapeHTMLPolicy = 'trustedTypes' in window ? trustedTypes.createPolicy('forceInner', { createHTML: html=>html }) : { createHTML: html=>html };
    }

    // NOTE: This renders the gui immediately instead of waiting for Show() to be called, useful for debugging added controls incrementally (as they are added)
    _guiDebugMode = false;

    // TODO: We should probably store a reference to the stylesheet element we create for this instance
    // _styleSheet = null;

    _getStylesheet() {
        const styleSheetId = `imgui-global-styles_${this.container.id}`;
        return document.getElementById(styleSheetId) || null;
    }

    constructor(options = {}) {
        this.options = {
            theme: 'dark',
            position: 'left', // left, right, center
            width: 300,
            draggable: true,
            title: 'Immediate GUI',

            // FIXME: form is not draggable when title is not left aligned
            titleLeftAligned: true,

            guiDebugMode: false,
            ...options
        };

        // NOTE: Used for querying controls by their assigned IDs and getting their values
        this.controlRegistry = new Map();

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

        // Gui debugging can now be enabled via the constructor options
        this._guiDebugMode = this.options.guiDebugMode;

        this.maxHeight = '85vh';

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

        this.toolbar = null;
        this.currentToolbarEntry = null;
        
        // Create main container
        this.container = this._compatCreateElement('div');
        this.container.id = ImmediateGUI._generateId();

        ImmediateGUI._applyGlobalStyles(this);
        //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 !important;
            -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.3s ease;
        `;

        this.container.style.top = '10px';
        switch (this.options.position) {
            case "left":
                this.container.style.right = 'unset';
                this.container.style.left = '10px';
                break;
            case "right":
                this.container.style.left = 'unset';
                this.container.style.right = '10px';
                break;
            case "center":
                this.container.style.right = 'unset';
                this.container.style.left = '50%';
                this.container.style.transform = 'translateX(-50%)';
                break;
        }

        this.titleBar = null;
        // FIXME: when the title is not left aligned, the form is not draggable
        this._createTitleBar(this.options.titleLeftAligned);
        
        this.contentContainer = this._compatCreateElement('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();
        }

        // NOTE: Allow incrementally debugging the GUI by rendering it immediately
        if (this._guiDebugMode) {
            this.Show();
        }
    }

    _registerControl(control, customId = null) {
        if (customId) {
            if (this.controlRegistry.has(customId)) {
                const existingControl = this.controlRegistry.get(customId);
                debugger;
                throw new Error('Tried to assign id "' + customId + '" to a control, but that ID is already in use by another control:', existingControl);
            }

            this.controlRegistry.set(customId, control);
            control.dataset.customId = customId;
        }
        else {
            // TODO: do we want to randomly assign an ID?
            // that would allow us to know exactly how many controls we have
            // in the current gui instance via the controlRegistry size
            // if we do, our override for .remove on the control should be done here also

            let generatedId = ImmediateGUI._generateId("id_");
            while (this.controlRegistry.has(generatedId)) {
                // TODO: remove generatedId from ImmediateGUI.OccupiedElementIds
                debugger;
                generatedId = ImmediateGUI._generateId("id_");
            }

            this.controlRegistry.set(generatedId, control);
            control.dataset.customId = generatedId;
        }

        control.remove = () => {
            const customId = control.dataset.customId;

            if (this.controlRegistry.has(customId)) {
                this.controlRegistry.delete(customId);
            }
            if (control.id) {
                ImmediateGUI.OccupiedElementIds = ImmediateGUI.OccupiedElementIds.filter(id => id !== control.id);
            }

            // Remove the control from our GUI
            if (control.parentElement && control.parentElement.classList.contains('imgui-wrapper')) {
                HTMLElement.prototype.remove.call(control.parentElement);
            }
            else {
                HTMLElement.prototype.remove.call(control);
            }
        };

        return control;
    }

    GetControlById(id) {
        return this.controlRegistry.get(id) || null;
    }

    GetControlValueById(id, defaultValue = null) {
        const control = this.GetControlById(id);
        if (!control) return defaultValue;
        
        // Handle different control types
        if (control.type === 'checkbox' || control.type === 'radio') {
            return control.checked;
        } else if (control.classList?.contains('imgui-progressbar')) {
            return control.getValue ? control.getValue() : control.value;
        } else if (control.getChecked) {
            // Radio buttons group
            return control.getChecked();
        } else if (control.getSelected) {
            // ListBox
            return control.getSelected();
        } 
        else if (control.type === 'number') {
            return parseFloat(control.value);
        }
        else if (control.tagName === 'IMG') {
            return control.src;
        }
        else if (control.tagName === 'UL') {
            return control.getSelected();
        }
        else if (control.classList?.contains('imgui-label') || control.classList?.contains('imgui-header')) {
            return control.textContent;
        }
        else {
            // Sliders, Datepickers, text inputs
            return control.value;
        }

        // TODO: How should buttons be handled?
        // TODO: How should separators be used?
    }

    _createTitleBar(leftAlign = true) {
        this.titleBar = this._compatCreateElement('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);
            height: var(--imgui-title-height);
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding-left: ${leftAlign ? '12px' : '0'};
            padding-right: 8px;
        `;

        // Create a wrapper for the title
        const titleWrapper = this._compatCreateElement('div');
        titleWrapper.style.cssText = `
            ${!leftAlign ? 'flex: 1; text-align: center;' : ''}
            margin-bottom: 5px;
        `;
        titleWrapper.textContent = this.options.title || 'ImmediateGUI';
        
        // Create minimize button
        const minimizeBtn = this._compatCreateElement('button');
        minimizeBtn.className = 'imgui-minimize-btn';
        minimizeBtn.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML('▼');
        minimizeBtn.title = "Minimize";
        minimizeBtn.style.cssText = `
            background: none;
            border: none;
            color: var(--imgui-text);
            font-size: 12px;
            cursor: pointer;
            padding: 4px 8px;
            margin-left: 10px;
            font-family: monospace;
        `;
        
        // minimizeBtn.addEventListener('mouseenter', () => {
        //     // minimizeBtn.style.opacity = '1';
        // });
        
        // minimizeBtn.addEventListener('mouseleave', () => {
        //     // minimizeBtn.style.opacity = '0.7';
        // });
        
        // Add click handler for minimizing
        this.isMinimized = false;
        minimizeBtn.addEventListener('click', (e) => {
            e.stopPropagation(); // Prevent triggering dragging
            this._toggleMinimize();
        });
       
        this.minimizeBtn = minimizeBtn;
        
        // Add elements to titlebar
        this.titleBar.appendChild(titleWrapper);
        this.titleBar.appendChild(minimizeBtn);
   
        this.container.appendChild(this.titleBar);
    }

    SetTitle(title) {
        const titleBarWrapper =  this.titleBar?.querySelector('div') ?? null;
        if (titleBarWrapper) {
            titleBarWrapper.textContent = title;
        }
    }

    _createToolbar() {
        // this.titleBar.style.marginBottom = '0px';
        // // NOTE: Should we keep the border bottom on the title bar when toolbar is present or not?
        // this.titleBar.style.borderBottom = 'none';

        this.toolbar = this._compatCreateElement('div');
        this.toolbar.className = 'imgui-toolbar';
        this.toolbar.id = ImmediateGUI._generateId('toolbar_');
        this.toolbar.style.cssText = `
            display: none;

            width: 100%;
            background: var(--imgui-section-bg);
            border-bottom: 1px solid var(--imgui-border);
            padding-top: 0px;
            padding-bottom: 4px;
            padding-left: 6px;
            padding-right: 6px;
            box-sizing: border-box;
            align-items: center;
            gap: 4px;
            flex-wrap: wrap;
            position: sticky;
            top: var(--imgui-title-height);
            z-index: 999999998;
            min-height: var(--imgui-toolbar-height);
            max-width: ${typeof this.options.width === 'string' ? this.options.width : this.options.width + 'px'};
            /*
            overflow-y: hidden; 
            overflow-x: auto;
            height: var(--imgui-toolbar-height);
            */

            margin-bottom: 12px; /* margin-bottom css is removed from the titlebar element and moved to the toolbar element */
        `;
        
        // Insert toolbar after title bar but before content
        this.container.insertBefore(this.toolbar, this.contentContainer);
    }
    _toggleToolbar(show = true) {
        if (!this.toolbar) return;

        if (show) {
            this.titleBar.style.marginBottom = '0px';
            this.titleBar.style.borderBottom = 'none';

            this.toolbar.style.minHeight = 'var(--imgui-toolbar-height)';
            this.toolbar.style.display = 'flex';
        }
        else {
            this.titleBar.style.marginBottom = '12px';
            this.titleBar.style.borderBottom = '1px solid var(--imgui-border)';

            this.toolbar.style.display = 'none';
        }
    }

    // TODO: This needs to be reworked to support nested dropdowns
    // ie. toolbar entry inside another toolbar entry
    _isInToolbarMode = false;
    BeginToolbarEntry(text, tooltip = '') {
        if (this._isInToolbarMode === true) { 
            // FIXME: either an user mistake or the api needs to be reworked to support nested toolbar entries (dropdowns inside dropdowns) 
            debugger;
            throw new Error('Already in toolbar entry mode. Nested toolbar entries are not currently supported.');
        }
        this._isInToolbarMode = true;

        if (!this.toolbar) {
            this._createToolbar();
        }

        if (this.toolbar.children.length === 0) {
            // Unhide the toolbar
            this.titleBar.style.marginBottom = '0px';
            this.titleBar.style.borderBottom = 'none';

            this.toolbar.style.minHeight = 'var(--imgui-toolbar-height)';
            this.toolbar.style.display = 'flex';
        }

        // Create dropdown container
        const dropdownContainer = this._compatCreateElement('div');
        dropdownContainer.className = 'imgui-toolbar-entry';
        dropdownContainer.id = ImmediateGUI._generateId('toolbar_entry_');
        // dropdownContainer.style.cssText = `/* FIXME: dropdown with items is clipped/hidden if the main gui container doesnt have big enough of height set */`;

        // Create the entry button (header)
        const entryButton = this._compatCreateElement('button');
        entryButton.className = 'imgui-button imgui-toolbar-entry-button';
        entryButton.textContent = text;
        entryButton.style.cssText = `
            font-size: 12px;
            padding: 4px 8px;
            background: var(--imgui-section-bg);
            border: none;
            cursor: pointer;
        `;
        if (tooltip) {
            entryButton.title = tooltip;
        }

        // Create dropdown menu container
        const dropdownMenu = this._compatCreateElement('div');
        dropdownMenu.className = 'imgui-toolbar-entry-menu';
        dropdownMenu.style.cssText = `
            position: absolute;
            top: 100%;
            left: 0;
            margin-top: 2px;
            background: var(--imgui-bg);
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            min-width: 120px;
            z-index: 10000;
            display: none;
            overflow: hidden;
            flex-direction: column;
        `;

        // Track open state
        let isOpen = false;

        // Toggle dropdown on click
        entryButton.addEventListener('click', (e) => {
            e.stopPropagation();
            isOpen = !isOpen;

            dropdownMenu.style.display = isOpen ? 'flex' : 'none';

            setTimeout(() => {
                // TODO: adjust height of this.contentContainer to fit the dropdown if needed
                try {
                    const dropdownHeight = getComputedStyle(dropdownMenu).getPropertyValue('height');
                    this.contentContainer.style.minHeight = isOpen ? dropdownHeight : 'unset';
                } catch (err) {
                    const fallbackHeight = dropdownMenu.offsetHeight > 0 ? dropdownMenu.offsetHeight + 'px' : '150px';
                    this.contentContainer.style.minHeight = isOpen ? fallbackHeight : 'unset';
                }
            }, 50);
            
            // Close other dropdowns
            if (isOpen && this.toolbar) {
                this.toolbar.querySelectorAll('.imgui-toolbar-entry-menu').forEach(menu => {
                    if (menu !== dropdownMenu) {
                        menu.style.display = 'none';
                    }
                });
            }
        });

        // Close dropdown when clicking outside
        const closeHandler = (e) => {
            if (isOpen && !dropdownContainer.contains(e.target)) {
                dropdownMenu.style.display = 'none';
                isOpen = false;
            }
        };

        document.addEventListener('click', closeHandler);
        
        // NOTE: Ensures removal of document wide click handler when dropdown is removed
        dropdownMenu.remove = () => {
            document.removeEventListener('click', closeHandler);
            HTMLElement.prototype.remove.call(dropdownMenu);
        };

        // Assemble the dropdown
        dropdownContainer.appendChild(entryButton);
        dropdownContainer.appendChild(dropdownMenu);
        this.toolbar.appendChild(dropdownContainer);

        // Store reference to current toolbar entry
        this.currentToolbarEntry = dropdownMenu;
        this.currentToolbarEntry._closeHandler = closeHandler;
        this.currentToolbarEntry._isOpen = () => isOpen;
        this.currentToolbarEntry._close = () => {
            dropdownMenu.style.display = 'none';
            isOpen = false;
        };

        return this;
    }
    EndToolbarEntry() {
        this._isInToolbarMode = false;

        if (!this.toolbar) return this;

        // Reset current toolbar entry reference
        this.currentToolbarEntry = null;

        return this;
    }

    /* 
    NOTE: Figure out if we want separate methods for toolbar controls or if the user
    should use the original control methods and instead add logic to each control method
    where we check if we are inside a toolbar entry (dropdown menu) and adjust the control
    styling/behavior accordingly.
    */
    // FIXME: Implement more toolbar controls
    ToolbarButton(text, callback, tooltip = '', id = null) {
        if (!this.toolbar) {
            this._createToolbar();
        }

        if (this.toolbar.children.length === 0) {
            this._toggleToolbar(true);
        }

        const button = this._compatCreateElement('button');
        button.className = 'imgui-button imgui-toolbar-button';
        button.innerText = text;
        
        // Different styling for dropdown menu items vs toolbar buttons
        if (this.currentToolbarEntry) {
            button.style.cssText = `
                font-size: 12px;
                padding: 8px 12px;
                background: transparent;
                border: none;
                width: 100%;
                text-align: left;
                cursor: pointer;
                transition: background 0.2s;

                border-radius: 0px !important;
            `;
            
            // Hover effect for menu items
            button.addEventListener('mouseenter', () => {
                button.style.background = 'var(--imgui-button-hover)';
            });
            button.addEventListener('mouseout', () => {
                button.style.background = 'transparent';
            });
        } else {
            button.style.cssText = `
                font-size: 12px;
                padding: 4px 8px;
                background: var(--imgui-section-bg);
                border: none;
            `;
        }
        
        if (tooltip && typeof tooltip === 'string') {
            button.title = tooltip;
        }
        
        // Wrap callback to close dropdown menu after click
        button.addEventListener('click', (e) => {
            if (callback && typeof callback === 'function') {
                callback(e);
            }
            
            // Close the dropdown menu if we're inside one
            if (this.currentToolbarEntry && this.currentToolbarEntry._close) {
                this.currentToolbarEntry._close();
            }
        });

        if (this.currentToolbarEntry) {
            this.currentToolbarEntry.appendChild(button);
        }
        else {
            this.toolbar.appendChild(button);
        }
        
        return this._registerControl(button, id);
    }
    ToolbarSeparator(horizontal = true) {
        if (!this.toolbar) {
            this._createToolbar();
        }

        if (this.toolbar.children.length === 0) {
            this._toggleToolbar(true);
        }

        const separator = this._compatCreateElement('div');
        separator.className = 'imgui-toolbar-separator';
        
        // NOTE: force horizontal separator if inside a toolbar entry (dropdown menu)
        horizontal = this.currentToolbarEntry ? true : horizontal;

        if (horizontal) {
            separator.style.cssText = `
                width: 100%;
                height: 1px;
                background: var(--imgui-border);
            `;
        }
        else {
            separator.style.cssText = `
                width: 2px;
                height: var(--imgui-toolbar-height);
                border-left: 1px solid var(--imgui-border);
            `;
        }
        
        if (this.currentToolbarEntry) {
            this.currentToolbarEntry.appendChild(separator);
        }
        else {
            this.toolbar.appendChild(separator);
        }

        return separator;
    }
    ToolbarCheckbox(label, checked = false, tooltip = '', onChangeCallback = null, id = null) {
        if (!this.toolbar) {
            this._createToolbar();
        }

        if (this.toolbar.children.length === 0) {
            this._toggleToolbar(true);
        }

        const wrapper = this._compatCreateElement('label');
        wrapper.className = 'imgui-toolbar-checkbox-wrapper';
        wrapper.style.cssText = `
            display: flex;
            align-items: center;
            font-size: 12px;
            cursor: pointer;
            padding: 4px 8px;
            
            border: none;
        `;
        wrapper.title = tooltip;

        const checkbox = this._compatCreateElement('input');
        checkbox.type = 'checkbox';
        checkbox.className = 'imgui-checkbox imgui-toolbar-checkbox';
        checkbox.checked = checked;
        checkbox.style.cssText = `
            margin-right: 6px;
            width: 12px;
            height: 12px;
            cursor: pointer;
        `;

        if (typeof onChangeCallback === 'function') {
            checkbox.addEventListener('change', onChangeCallback);
        }
        
        const labelEl = this._compatCreateElement('span');
        labelEl.textContent = label;

        // NOTE: figure out if we can apply this in all cases (prevents extra space below the control when not in a toolbar entry)
        if (!this.currentToolbarEntry) {
            wrapper.style.marginBottom = '0px';
        }

        wrapper.appendChild(checkbox);
        wrapper.appendChild(labelEl);

        if (this.currentToolbarEntry) {
            this.currentToolbarEntry.appendChild(wrapper);
        }
        else {
            this.toolbar.appendChild(wrapper);
        }

        return this._registerControl(checkbox, id);
    }
    // FIXME: see comment inside ToolbarSwitch
    ToolbarSwitch(label, checked = false, tooltip = '', onChangeCallback = null, id = null) {
        if (!this.toolbar) {
            this._createToolbar();
        }

        if (this.toolbar.children.length === 0) {
            this._toggleToolbar(true);
        }

        const wrapper = this._compatCreateElement('label');
        wrapper.className = 'imgui-toolbar-switch-wrapper';
        wrapper.style.cssText = `
            display: flex;
            align-items: center;
            font-size: 12px;
            cursor: pointer;
            padding: 4px 8px;
            border: none;
        `;
        wrapper.title = tooltip;
        const switchInput = this._compatCreateElement('input');
        switchInput.type = 'checkbox';
        switchInput.className = 'imgui-switch imgui-toolbar-switch';
        switchInput.checked = checked;
        switchInput.style.cssText = `
            position: relative;
            width: 30px;
            height: 16px;
            appearance: none;
            background: var(--imgui-section-bg);
            outline: none;
            border-radius: 8px;
            transition: background 0.2s;
            margin-right: 6px;
            border: 1px solid var(--imgui-border);
        `;

        const switchThumb = this._compatCreateElement('span');
        switchThumb.className = 'imgui-switch-thumb';
        switchThumb.style.cssText = `
            left: 14px; /* DEBUG:  */

            position: absolute;
            width: 12px;
            height: 12px;
            background: var(--imgui-accent);
            border-radius: 50%;
            transition: transform 0.2s;
        `;

        // FIXME: the background color for switchThumb needs to be revised
        // in its unchecked state, it looks fine, but in the checked state it
        // fills the entire width of the switch, which looks odd
        const leftOffset = switchThumb.style.left || '14px';
        switchInput.addEventListener('change', () => {
            if (switchInput.checked) {
                switchInput.style.background = 'var(--imgui-accent)';
                switchThumb.style.transform = `translateX(${leftOffset})`;
            } else {
                // TODO: this needs to be adjusted, see comment above
                switchInput.style.background = 'var(--imgui-section-bg)';
                switchThumb.style.transform = 'translateX(0)';
            }
        });
        if (switchInput.checked) {
            switchInput.style.background = 'var(--imgui-accent)';
            switchThumb.style.transform = `translateX(${leftOffset})`;
        }
        wrapper.appendChild(switchInput);
        wrapper.appendChild(switchThumb);
        const labelEl = this._compatCreateElement('span');
        labelEl.textContent = label;
        wrapper.appendChild(labelEl);
        if (this.currentToolbarEntry) {
            this.currentToolbarEntry.appendChild(wrapper);
        }
        else {
            this.toolbar.appendChild(wrapper);
        }
        return this._registerControl(switchInput, id);
    }

    
    _flashTitleBar(duration = 2000, flashCount = 6) {
        if (!this.titleBar) return this;
        
        const originalBackground = this.titleBar.style.background;
        const flashBackground = `#eb6b09`;
        const flashInterval = duration / (flashCount * 2); // *2 because each flash has on/off states
        
        let currentFlash = 0;
        
        const flash = () => {
            if (currentFlash >= flashCount * 2) {
                // Restore original background
                this.titleBar.style.background = originalBackground;
                this.titleBar.style.transition = '';
                return;
            }
            
            // Toggle between flash color and original color
            if (currentFlash % 2 === 0) {
                this.titleBar.style.background = flashBackground;
            } else {
                this.titleBar.style.background = originalBackground;
            }
            
            currentFlash++;
            setTimeout(flash, flashInterval);
        };
        
        // Add smooth transition for the flash effect
        this.titleBar.style.transition = 'background-color 0.1s ease';
        
        // Start flashing
        flash();
        
        return this;
    }

    _toggleMinimize(forceState = null) {
        this.isMinimized = !forceState ? !this.isMinimized : (typeof forceState === 'boolean' ? forceState : !this.isMinimized);
        
        if (this.isMinimized) {
            // Minimize the GUI
            this.contentContainer.style.display = 'none';
            this.container.style.maxHeight = 'var(--imgui-title-height)';
            this.container.style.top = 'auto';
            this.container.style.bottom = '10px';
            this.minimizeBtn.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML('▲');
            this.minimizeBtn.title = "Restore";
            this.titleBar.style.marginBottom = '0';
            this.titleBar.style.borderBottom = 'none';
            this.container.style.overflowY = 'hidden';
            
            this._flashTitleBar(1000, 3);
        } else {
            // Restore the GUI
            this.contentContainer.style.display = 'block';
            this.container.style.maxHeight = this.maxHeight;
            this.container.style.bottom = 'auto';
            this.container.style.top = '10px';
            this.minimizeBtn.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML('▼');
            this.minimizeBtn.title = "Minimize";
            this.titleBar.style.marginBottom = '12px';
            this.titleBar.style.borderBottom = '1px solid var(--imgui-border)';
            this.container.style.overflowY = 'auto';
        }
        
        // Make sure the GUI stays in view
        this._keepInView();
    }

    // NOTE: Make sure this actually works correctly
    _updateCSSVariables() {
        // let styleSheet = this._getStylesheet();
        
        // We modify the container styles directly to support multiple instances
        this.container.style.setProperty('--imgui-bg', this.theme.background);
        this.container.style.setProperty('--imgui-text', this.theme.text);
        this.container.style.setProperty('--imgui-border', this.theme.border);
        this.container.style.setProperty('--imgui-accent', this.theme.accent);
        this.container.style.setProperty('--imgui-button-bg', this.theme.buttonBg);
        this.container.style.setProperty('--imgui-button-hover', this.theme.buttonHover);
        this.container.style.setProperty('--imgui-input-bg', this.theme.inputBg);
        this.container.style.setProperty('--imgui-section-bg', this.theme.sectionBg);
    }

    static _applyGlobalStyles(guiInstance = null) {
        // If guiInstance is null, we are applying global styles for static usage
        const isStatic = guiInstance === null;

        // TODO: since the nonstatic style sheets can be used
        // for static only purposes also the logic here could be reworked

        if (isStatic) {
            if (ImmediateGUI.HasStaticStyles) return;
            if (document.getElementById(ImmediateGUI.StaticStyleSheetId)) {
                ImmediateGUI.HasStaticStyles = true;
                return;
            }

            // This defaults to the dark theme
            const defaultThemeName = 'dark';
            const placeHolderThemes = {
                light: {
                    background: '#ffffff',
                    text: '#333333',
                    border: '#cccccc',
                    accent: '#4285f4',
                    buttonBg: '#f5f5f5',
                    buttonHover: '#e0e0e0',
                    inputBg: '#ffffff',
                    sectionBg: '#f9f9f9'
                },
                dark: {
                    background: '#151617',
                    text: '#eef0f2',
                    border: '#425069',
                    accent: '#294a7a',
                    buttonBg: '#274972',
                    buttonHover: '#336caf',
                    inputBg: '#20324d',
                    sectionBg: '#232426',
                }
            };

            const theme = placeHolderThemes[defaultThemeName];

            const styleEl = ImmediateGUI._compatCreateElement('style');
            styleEl.id = ImmediateGUI.StaticStyleSheetId;
            styleEl.textContent = `
                :root {
                    --imgui-bg: ${theme.background};
                    --imgui-text: ${theme.text};
                    --imgui-border: ${theme.border};
                    --imgui-accent: ${theme.accent};
                    --imgui-button-bg: ${theme.buttonBg};
                    --imgui-button-hover: ${theme.buttonHover};
                    --imgui-input-bg: ${theme.inputBg};
                    --imgui-section-bg: ${theme.sectionBg};

                    --imgui-toolbar-height: 25px;
                    --imgui-bottom-padding: 3px;
                    --imgui-title-height: 30px;
                    --imgui-scrollbar-width: 0.5em;
                }

                /* For the non static css, we also include some CSS styles that "resets", we dont do that here
                and that might cause visual inconsistencies 
                */


                .imgui-toast {
                    /* additional styling for toasts specifically can go here */
                }
            `;
            (document.head || document.getElementsByTagName('head')[0] || document.documentElement).appendChild(styleEl);
            ImmediateGUI.HasStaticStyles = true;
            return;
        }

        // NOTE: Here we handle instances based styles

        // NOTE: We should not remove the static styles, as we changed how we store 
        // our css variables to be instance based instead of global, meaning our static methods
        // wont be able to use the static styles anymore, instead we should migrate the current instance styles
        // to the static styles
        if (ImmediateGUI.HasStaticStyles) {
            // TODO: Migrate current instance styles to static styles   
            debugger;
        }
        //document.getElementById(ImmediateGUI.StaticStyleSheetId)?.remove();
        // NOTE: ImmediateGUI.HasStaticStyles can remain true, as method who are called statically
        // will be able to use the non "static" styles
        const styleSheetId = `imgui-global-styles_${guiInstance.container.id}`;

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

            styleEl.id = styleSheetId;
            styleEl.textContent = `
                #${guiInstance.container.id} {
                /* :root { */
                    --imgui-bg: ${guiInstance.theme.background};
                    --imgui-text: ${guiInstance.theme.text};
                    --imgui-border: ${guiInstance.theme.border};
                    --imgui-accent: ${guiInstance.theme.accent};
                    --imgui-button-bg: ${guiInstance.theme.buttonBg};
                    --imgui-button-hover: ${guiInstance.theme.buttonHover};
                    --imgui-input-bg: ${guiInstance.theme.inputBg};
                    --imgui-section-bg: ${guiInstance.theme.sectionBg};

                    --imgui-toolbar-height: 25px;
                    --imgui-bottom-padding: 3px;
                    --imgui-title-height: 30px;
                    --imgui-scrollbar-width: 0.5em;
                }

                #${guiInstance.container.id} {
                    /* CSS Reset for all controls inside our GUI */
                    &::*, & *::before, & *::after {
                        box-sizing: border-box;
                        margin: 0;
                    }

                    &::* {
                        margin: 0;
                        padding: 0px;
                        outline: none;
                        -webkit-font-smoothing: antialiased;
                    }

                    &::input, &::button, &::textarea, &::select {
                        font: inherit;
                    }

                    &::p, &::h1, &::h2, &::h3, &::h4, &::h5, &::h6 {
                        overflow-wrap: break-word;
                    }

                    &::p {
                        text-wrap: pretty;
                    }
                    &::h1, &::h2, &::h3, &::h4, &::h5, &::h6 {
                        text-wrap: balance;
                    }

                    &::#root, &::#__next {
                        isolation: isolate;
                    }
                    /* End of CSS Reset */

                    scrollbar-gutter: auto;
                    /* scrollbar-color: var(--imgui-accent); */
                    /* scrollbar-width: thin; */

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

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

                    &::-webkit-scrollbar-thumb:hover {
                        /* background-color: var(--imgui-button-hover); */
                    }

                    &::-webkit-scrollbar-button:start:decrement {
                        height: calc(var(--imgui-title-height) + ${guiInstance.toolbar ? 'var(--imgui-toolbar-height)' : '1px' /* essentially 0px as we subtract 1px */} - 1px);
                        display: block;
                        background-color: transparent;
                    }
                }

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

                .imgui-toolbar-button:hover {
                    background: var(--imgui-button-hover) !important;
                }

                .imgui-progressbar {

                }

                .imgui-image {
                
                }

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

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

                .imgui-checkbox {
                    
                }

                .imgui-radiobutton {
                
                }

                .imgui-control {
                    /* margin-bottom: 2px; */
                    width: 100%;
                }

                .imgui-control[disabled] {
                    cursor: not-allowed;
                    /* FIXME: we need to disable background color change events */
                }

                .imgui-wrapper {
                    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;
                    font-family: inherit !important;
                    cursor: pointer;
                    transition: all 0.2s ease;
                    outline: none;
                    width: auto;
                    font-family: inherit;
                    text-transform: none !important;
                    /* margin-right: 5px; */
                }

                .imgui-toolbar-entry {
                    position: relative;
                    display: inline-block;
                }

                .imgui-toolbar-entry-button:hover {
                    background: var(--imgui-button-hover) !important;
                }

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

                /* Chrome/Safari styling
                // TODO: Add firefox styling also */
                .imgui-input[type="number"]::-webkit-inner-spin-button,
                .imgui-input[type="number"]::-webkit-outer-spin-button {
                    -webkit-appearance: none;
                    -moz-appearance: none;
                    appearance: none;
                    margin: 0;
                }

                /* Chrome/Safari styling 
                // TODO: Add firefox styling also */
                .imgui-input[type="number"]::-webkit-textfield-decoration-container {
                    /* border: 1px var(--imgui-border) solid; */
                    background: var(--imgui-input-bg);
                }
                
                .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: calc(var(--imgui-bottom-padding) * 2);
                    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;

                    /* 
                    pointer-events: none !important;
                    -webkit-touch-callout:none !important;
                    -webkit-user-select:none !important
                    -khtml-user-select:none !important;
                    -moz-user-select:none !important;
                    -ms-user-select:none !important;
                    user-select:none !important;
                    */
                }
            `;

            (document.head || document.getElementsByTagName('head')[0] || document.documentElement).appendChild(styleEl);
            guiInstance.container.classList.add('imgui-container');
        }
    }

    _setupDragging() {
        let isDragging = false;
        let startX, startY, startLeft, startTop;
        let rafId = null;
        let currentX = 0, currentY = 0;

        const isClickOnControl = (element) => {
            if (!element) return false;
            
            // NOTE: only trigger dragging if the click was on the title bar element
            const result = !element.classList.contains('imgui-titlebar');
            return result;
        };

        const updatePosition = () => {
            const dx = currentX - startX;
            const dy = currentY - 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';
            
            rafId = null;
        };

        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;
            
            // NOTE: container has transition set to 'all 0.3s ease' by default
            this.container.style.transition = 'none'; // Disable transitions during drag

            document.body.style.cursor = 'move';
            
            e.preventDefault();
        });
        
        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            
            // Just store the mouse position, don't update DOM yet
            currentX = e.clientX;
            currentY = e.clientY;
            
            // Schedule update if not already scheduled
            if (!rafId) {
                rafId = requestAnimationFrame(updatePosition);
            }
        });
        
        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                document.body.style.cursor = '';
                
                // Cancel any pending animation frame
                if (rafId) {
                    cancelAnimationFrame(rafId);
                    rafId = null;
                }
                
                this.container.style.transition = 'all 0.3s ease'; // Re-enable transitions after drag
                this._keepInView();
            }
        });
        
        // document.addEventListener('mouseleave', () => {
        //     if (isDragging) {
        //         isDragging = false;
        //         document.body.style.cursor = '';
        //     }
        // });

        // document.addEventListener('mouseout', () => {
        //     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, tooltip = '', id = null) {
        const section = this._compatCreateElement('div');
        section.className = 'imgui-section';
        section.id = ImmediateGUI._generateId('section_');
        section.style.paddingBottom = 'var(--imgui-bottom-padding)';
        if (typeof tooltip === 'string' && tooltip.length > 0) section.title = tooltip;

        if (title) {
            const header = this._compatCreateElement('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 collapseCharacter = '▼';
                const uncollapseCharacter = '►';

                const indicator = this._compatCreateElement('span');
                indicator.className = 'imgui-section-indicator';
                indicator.textContent = collapseCharacter;
                indicator.style.cssText = `
                    margin-right: 8px;
                    font-size: 10px;
                    font-family: monospace;
                    transition: transform 0.2s ease;
                `;
                
                const titleSpan = this._compatCreateElement('span');
                titleSpan.textContent = title;
                titleSpan.style.flex = '1';
                
                const content = this._compatCreateElement('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 = uncollapseCharacter;
                        indicator.style.transform = 'rotate(0deg)';
                        header.style.borderBottom  = 'none';
                        header.style.paddingBottom = '0px';

                    } else {
                        content.style.maxHeight = '2000px';
                        indicator.textContent = collapseCharacter; 
                        indicator.style.transform = 'rotate(0deg)';
                        header.style.borderBottom  = '1px solid var(--imgui-border)';
                        header.style.paddingBottom = '6px';
                    }
                };

                if (collapsedByDefault) toggleCollapse();
                
                header.addEventListener('click', toggleCollapse);
                section.toggleCollapse = 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);

        section.collapsible = collapsible;
        section.isCollapsed = collapsedByDefault;

        this.currentSection = section;

        return this._registerControl(section, id)
    }
    EndSection() {
        const parentSectionOrNull = this.currentSection ? this.currentSection.parentElement.closest('.imgui-section') : null;
        // NOTE: We set currentSection to parent section to allow nested sections
        this.currentSection = parentSectionOrNull;
        return this;
    }

    // Row management
    BeginRow(gap = 2) {
        const row = this._compatCreateElement('div');
        row.className = 'imgui-row';
        row.id = ImmediateGUI._generateId('row_');
        row.style.cssText = `
            display: flex;
            flex-direction: row;
            align-items: flex-start;
            justify-content: center;
            ${gap > 0 ? `gap: ${gap}px;` : 'gap: var(--imgui-bottom-padding);'}
            width: 100%;
        `;
        
        this._getTargetContainer().appendChild(row);
        this.currentRow = row;
        return this;
    }
    EndRow() {
        // Post processing for items in the row
        if (this.currentRow) {
            // TODO: set all children of the row to have the same height as the biggest child inside the row

            this.currentRow.querySelectorAll('.imgui-wrapper').forEach((wrapper) => {
                wrapper.style.flex = '1';
                wrapper.style.marginRight = '0px !important';
                wrapper.querySelectorAll('.imgui-control').forEach((control) => {
                    control.style.width = '100%';
                });
            });
        }

        this.currentRow = 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;
            // TODO: make sure element with its new margin left wont exceed past the bounds of the container
            
            element.style.marginLeft = `${currentIndent}px`;
        }
        return element;
    }

    // Tabs management
    BeginTabs(tabs = [], defaultTab = 0) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper imgui-tabs";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            background: var(--imgui-section-bg);
        `;
        
        // Create tab headers
        const tabHeaders = this._compatCreateElement('div');
        tabHeaders.className = "imgui-tab-headers";
        tabHeaders.style.cssText = `
            display: flex;
            border-bottom: 1px solid var(--imgui-border);
            background: var(--imgui-input-bg);
        `;
        
        // Create tab contents
        const tabContents = this._compatCreateElement('div');
        tabContents.className = "imgui-tab-contents";
        tabContents.style.cssText = `
            padding: 10px;
            padding-bottom: calc(10px - var(--imgui-bottom-padding));
        `;
        
        this.tabPanes = [];
        let activeTab = defaultTab;
        
        tabs.forEach((tab, index) => {
            // Create tab header
            const tabHeader = this._compatCreateElement('div');
            tabHeader.textContent = tab;
            tabHeader.style.cssText = `
                padding: 10px 16px;
                cursor: pointer;
                border-right: 1px solid var(--imgui-border);
                /*
                border-radius: 5px 5px 0px 0px;
                -webkit-border-radius: 5px 5px 0px 0px;
                -moz-border-radius: 5px 5px 0px 0px;
                */
                background: ${index === activeTab ? 'var(--imgui-accent)' : 'transparent'};
                color: var(--imgui-text);
                transition: background 0.2s ease;
            `;

            
            // Create tab content pane
            const tabPane = this._compatCreateElement('div');
            tabPane.className = "imgui-tab-pane";
            tabPane.style.display = index === activeTab ? 'block' : 'none';
            tabPane.tabName = tabHeader.textContent;

            tabHeader.addEventListener('click', () => {
                // Hide all panes
                this.tabPanes.forEach(pane => pane.style.display = 'none');
                // Show selected pane
                tabPane.style.display = 'block';
                // Update header styles
                tabHeaders.querySelectorAll('div').forEach(header => {
                    header.style.background = 'transparent';
                });
                tabHeader.style.background = 'var(--imgui-accent)';
                activeTab = index;
            });
            
            tabHeaders.appendChild(tabHeader);
            tabContents.appendChild(tabPane);
            this.tabPanes.push(tabPane);
        });
        
        wrapper.appendChild(tabHeaders);
        wrapper.appendChild(tabContents);
        
        this._getTargetContainer().appendChild(wrapper);
        this.currentTab = { 
            wrapper, 
            panes: this.tabPanes, 
            activeTab,
            currentTabIndex: 0 // Track which tab we're currently adding content to
        };

        // TODO: set this.currentSection to tab with the index in the 
        // 'defaultTab' function parameter, this means validating the value of
        // 'defaultTab' also

        this.currentSection = this.tabPanes[0]; // Start with first tab
        return this;
    }
    SetActiveTab(tabIndexOrTabName) {
        const tabIndexIsNumber = typeof tabIndexOrTabName === 'number';

        if (!tabIndexIsNumber) { 
            const tabs = this.tabPanes;
            tabIndexOrTabName = tabs.findIndex(tab => tab.tabName === tabIndexOrTabName);
            if (tabIndexOrTabName === -1) {
                console.error(`ImmediateGUI: Invalid tab name specified: ${tabIndexOrTabName}`);
                return this;
            }
        }
        
        if (this.currentTab && tabIndexOrTabName >= 0 && tabIndexOrTabName < this.currentTab.panes.length) {
            this.currentTab.currentTabIndex = tabIndexOrTabName;
            this.currentSection = this.currentTab.panes[tabIndexOrTabName];
        }
        else {
            // TODO: Figure out proper error handling here
            console.error(`ImmediateGUI: Invalid tab index specified: ${tabIndexOrTabName}`);
        }
        
        return this;
    }
    EndTabs() {
        this.currentTab = null;
        this.currentSection = null;
        return this;
    }

    // Utility to get current target container
    _getTargetContainer() {
        if (this.currentRow) {
            return this.currentRow;
        }

        if (this.currentTab) {
            // Use the tab we're currently building, not the active displayed tab
            return this.currentTab.panes[this.currentTab.currentTabIndex];
        }
        
        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;
    }

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

    // FIXME: Figure out how to implement this
    // OfType expects a case sensitive string representing the type of control, matching the function name used to create that control
    _getControls(OfType = null) {
        // FIXME: we can now get all controls (and their wrappers) via this.controlRegistry
        // as elements who dont get an explicit ID passed to them get an auto generated ID

        const controlWrappers = this._getControlContainer().querySelectorAll('.imgui-wrapper');

        // if (OfType) {
        //     const lookupTable = {
        //         'Button': 'button',
        //         'Textbox': 'input[type="text"]',
        //         'TextArea': 'textarea',
        //         'Label': 'label',
        //         'ProgressBar': '.imgui-progressbar',
        //         'Checkbox': '.imgui-checkbox',
        //         'Radiobutton': '.imgui-radiobutton',
        //         'Slider': '.imgui-slider',
        //         'Image': '.imgui-image',
        //         'Separator': 'hr',
        //     };
        //     const selector = lookupTable[OfType];
        //     const matchedControls = Array.from(controlWrappers).map(wrapper => wrapper.querySelector(selector)).filter(control => control !== null);
        //     debugger;
        // }
        return controlWrappers;
    }

    // TODO: UX Improvement: apply the tooltip to the wrapper instead of the individual control
    // that way the user can hover over the whole area of the control to see the tooltip
    // instead of having to hover exactly over the control element itself

    Separator(plain = false, id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            width: 100%;
            margin-bottom: var(--imgui-bottom-padding);

            /* TODO: the height should be dynamically calculated instead of hardcoded like this for 'plain' separators */
            ${plain ? 'height: 11px !important;' : ''}
        `;
        
        const separator = this._compatCreateElement('hr');
        separator.id = ImmediateGUI._generateId("ctrl_");
        separator.className = 'imgui-separator';
        separator.style.cssText = `
            border: none;
            ${plain ? '' : 'border-top: 1px solid var(--imgui-border);'}
            /* margin: 10px 0; */
            width: 100%;
        `;
        
        wrapper.appendChild(separator);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(separator, id);
    }

    Header(text, level = 1, id = null) {
        const wrapper = this._compatCreateElement('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 = this._compatCreateElement(`h${validLevel}`);
        header.id = ImmediateGUI._generateId("ctrl_");
        header.className = 'imgui-header';
        header.textContent = text;
        header.style.cssText = `
            /* margin: 0 0 10px 0; */
            padding: 0;
            font-weight: ${validLevel <= 2 ? 'bold' : '600'};
            color: var(--imgui-text);
            font-size: ${24 - (validLevel * 2)}px;
            font-family: inherit;
            width: 100%;
        `;

        wrapper.appendChild(header);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(header, id);
    }

    Button(text, callback, tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const btn = this._compatCreateElement('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.style.background = 'var(--imgui-button-bg)';
        btn.style.border = '1px solid var(--imgui-border)';
        btn.style.color = 'var(--imgui-text)';

        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 this._registerControl(btn, id);
    }

    Textbox(placeholder, defaultValue = "", tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const input = this._compatCreateElement('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.style.fontFamily = 'inherit !important';
        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 this._registerControl(input, id);
    }

    TextArea(placeholder = "", defaultValue = "", rows = 4, tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const textarea = this._compatCreateElement('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;
            margin-bottom: 0;
            max-height: calc(${this.maxHeight} - 50px); /* Limit max height to prevent overflowing */
        `;
        textarea.style.fontFamily = 'inherit !important';
        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`;
            }
        });

        textarea.addEventListener('mouseenter', () => {
            textarea.style.borderColor = 'var(--imgui-border)';
        });

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

        return this._registerControl(textarea, id);
    }

    Label(text, id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const label = this._compatCreateElement('label');
        label.id = ImmediateGUI._generateId("ctrl_");
        label.textContent = text;
        label.className = "imgui-label imgui-control";
        label.style.fontFamily = 'inherit !important';
        
        wrapper.appendChild(label);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(label, id);
    }

    ProgressBar(value = 0, min = 0, max = 100, showText = true, tooltip = '', id = null) {
        // Create wrapper element
        const wrapper = this._compatCreateElement('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 = this._compatCreateElement('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
        let progressBar = this._compatCreateElement('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.value = normalizedValue;
        progressBar.max = max;
        progressBar.min = min;
        
        // Note: calling increment/decremenet will change the value by 1% unit, should be customizable
        //progressBar.step = (max - min) / 100; // Default step is 1% of the range
        progressBar.step = 1; // Default step is 1 unit

        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 = this._compatCreateElement('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 var(--imgui-text);
                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}%`;
            progressBar.value = normalizedNewValue;
            if (textElement) {
                textElement.textContent = `${Math.round(newPercentage)}%`;
            }
        };

        progressBar.getValue = () => progressBar.value;

        progressBar.increment = () => {
            const newValue = Math.min(progressBar.value + progressBar.step, max);
            progressBar.setValue(newValue);
        }

        progressBar.decrement = () => {
            const newValue = Math.max(progressBar.value - progressBar.step, min);
            progressBar.setValue(newValue);
        }
        
        // Store references
        progressBar.textElement = textElement;
        progressBar.min = min;
        progressBar.max = max;
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(progressBar, id);
    }

    ColorPicker(defaultValue = '#000000', tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; align-items: center; padding-bottom: var(--imgui-bottom-padding);`;
        
        const colorPicker = this._compatCreateElement('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 = this._compatCreateElement('span');
        colorValue.textContent = defaultValue;
        colorValue.style.cssText = `
            font-family: monospace;
            color: var(--imgui-text);
            cursor: pointer;
        `;

        // TODO: implement copying the color value to clipboard?

        colorPicker.addEventListener('input', () => {
            colorValue.textContent = colorPicker.value;
        });
        
        wrapper.appendChild(colorPicker);
        wrapper.appendChild(colorValue);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(colorPicker, id);
    }

    DatePicker(defaultValue = new Date().toISOString().split('T')[0], tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const datePicker = this._compatCreateElement('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 this._registerControl(datePicker, id);
    }

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

        const select = this._compatCreateElement('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 !important;
            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 = this._compatCreateElement('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 this._registerControl(select, id);
    }

    NumberInput(label, defaultValue = 0, min = null, max = null, tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `display: flex; align-items: center; margin-bottom: var(--imgui-bottom-padding);`;
        
        const labelElem = this._compatCreateElement('label');
        labelElem.textContent = label;
        labelElem.style.cssText = `
            margin-right: 10px;
            margin-top:8px;
            flex: 1;
            color: var(--imgui-text);
        `;
        
        const input = this._compatCreateElement('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 = parseFloat(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);
                    }
                }
            }, true); /* capture = true to ensure all validation happens before user added callbacks */
        }

        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);
        
        // NOTE: Applying the indentation to the label instead of the wrapper like in every other method has a purpose, note to self
        this._applyIndentation(labelElem);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(input, id);
    }

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

    Checkbox(label, checked = false, tooltip = '', onChangeCallback = null, id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex; 
            align-items: center; 
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const checkbox = this._compatCreateElement('input');
        checkbox.id = ImmediateGUI._generateId("ctrl_");
        checkbox.type = 'checkbox';
        checkbox.checked = checked;
        checkbox.className = "imgui-checkbox";
        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 = this._compatCreateElement('label');
        labelElem.textContent = label;
        labelElem.htmlFor = checkbox.id;
        labelElem.style.cssText = `
            cursor: pointer;
            color: var(--imgui-text);
            font-family: inherit;

            /* this causes inconsistencies visually, needs to be automatically calculated */
            margin-top: 6px;
        `;

        checkbox.label = labelElem;

        if (typeof onChangeCallback === 'function') {
            checkbox.addEventListener('change', () => {
                onChangeCallback(checkbox.checked);
            });
        }
        
        wrapper.appendChild(checkbox);
        wrapper.appendChild(labelElem);
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(checkbox, id);
    }

    ToggleSwitch(label, checked = false, tooltip = '', onChangeCallback = null, id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            align-items: center;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        // Hidden input for form compatibility
        const hiddenInput = this._compatCreateElement('input');
        hiddenInput.id = ImmediateGUI._generateId("ctrl_");
        hiddenInput.type = 'checkbox';
        hiddenInput.checked = checked;
        hiddenInput.style.display = 'none';
        hiddenInput.className = "imgui-toggle-input";
        
        // Create the visual toggle switch
        const toggleTrack = this._compatCreateElement('div');
        toggleTrack.className = "imgui-toggle-track";
        toggleTrack.style.cssText = `
            width: 48px;
            height: 24px;
            background: ${checked ? 'var(--imgui-accent)' : 'var(--imgui-border)'};
            border-radius: 12px;
            position: relative;
            cursor: pointer;
            transition: background-color 0.3s ease;
            margin-right: 12px;
            border: 1px solid var(--imgui-border);
            box-sizing: border-box;
        `;
        
        const toggleThumb = this._compatCreateElement('div');
        toggleThumb.className = "imgui-toggle-thumb";
        toggleThumb.style.cssText = `
            width: 20px;
            height: 20px;
            background: var(--imgui-text);
            border-radius: 50%;
            position: absolute;
            top: 1px;
            left: ${checked ? '25px' : '1px'};
            transition: left 0.3s ease;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        `;
        
        // Label element
        const labelElem = this._compatCreateElement('label');
        labelElem.textContent = label;
        labelElem.htmlFor = hiddenInput.id;
        labelElem.style.cssText = `
            cursor: pointer;
            color: var(--imgui-text);
            font-family: inherit;
            user-select: none;
            flex: 1;

            /* //FIXME: shitty hack to vertically align label with its assosciated input element */
            padding-bottom: 2px;
            margin: auto;
        `;
        
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) {
            wrapper.title = tooltip;
        }
        
        // Add CSS for hover effects if not already added
        if (!document.getElementById('imgui-toggle-styles')) {
            const toggleStyles = this._compatCreateElement('style');
            toggleStyles.id = 'imgui-toggle-styles';
            toggleStyles.textContent = `
                .imgui-toggle-track:hover {
                    box-shadow: 0 0 0 2px rgba(var(--imgui-accent-rgb, 41, 74, 122), 0.2);
                }
                
                .imgui-toggle-track:active .imgui-toggle-thumb {
                    transform: scale(0.95);
                }
            `;
            document.head.appendChild(toggleStyles);
        }
        
        // Toggle functionality
        const toggle = () => {
            const newChecked = !hiddenInput.checked;
            hiddenInput.checked = newChecked;
            
            // Update visual state
            if (newChecked) {
                toggleTrack.style.background = 'var(--imgui-accent)';
                toggleThumb.style.left = '25px';
            } else {
                toggleTrack.style.background = 'var(--imgui-border)';
                toggleThumb.style.left = '1px';
            }
            
            // Dispatch change event
            hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
        };
        
        // Event listeners
        toggleTrack.addEventListener('click', toggle);
        labelElem.addEventListener('click', (e) => {
            e.preventDefault(); // Prevent double toggle
            toggle();
        });
        
        // Keyboard accessibility
        toggleTrack.setAttribute('tabindex', '0');
        toggleTrack.setAttribute('role', 'switch');
        toggleTrack.setAttribute('aria-checked', checked);
        toggleTrack.setAttribute('aria-labelledby', hiddenInput.id + '_label');
        labelElem.id = hiddenInput.id + '_label';
        
        toggleTrack.addEventListener('keydown', (e) => {
            if (e.key === ' ' || e.key === 'Enter') {
                e.preventDefault();
                toggle();
            }
        });
        
        // Update aria-checked when state changes
        hiddenInput.addEventListener('change', () => {
            toggleTrack.setAttribute('aria-checked', hiddenInput.checked);
            if (onChangeCallback && typeof onChangeCallback === 'function') {
                onChangeCallback(hiddenInput.checked);
            }
        });
        
        // Add hover effects
        toggleTrack.addEventListener('mouseenter', () => {
            if (!hiddenInput.checked) {
                toggleTrack.style.background = 'var(--imgui-button-hover)';
            }
        });

        toggleTrack.addEventListener('mouseout', () => {
            if (!hiddenInput.checked) {
                toggleTrack.style.background = 'var(--imgui-border)';
            }
        });
        
        // Focus styles
        toggleTrack.addEventListener('focus', () => {
            toggleTrack.style.outline = '2px solid var(--imgui-accent)';
            toggleTrack.style.outlineOffset = '2px';
        });
        
        toggleTrack.addEventListener('blur', () => {
            toggleTrack.style.outline = 'none';
        });
        
        // Build the component
        toggleTrack.appendChild(toggleThumb);
        wrapper.appendChild(hiddenInput);
        wrapper.appendChild(toggleTrack);
        wrapper.appendChild(labelElem);
        
        // Add helper methods to the hidden input for API consistency
        hiddenInput.toggle = toggle;
        hiddenInput.setChecked = (value) => {
            if (value !== hiddenInput.checked) {
                toggle();
            }
        };
        hiddenInput.label = labelElem;
        hiddenInput.track = toggleTrack;
        hiddenInput.thumb = toggleThumb;
        
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(hiddenInput, id);
    }

    RadioButtons(options = [], defaultValue = null, tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('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 = this._compatCreateElement('div');
            radioContainer.style.cssText = `
                display: flex;
                align-items: center;
            `;
            
            const radio = this._compatCreateElement('input');
            radio.id = ImmediateGUI._generateId("ctrl_");
            radio.type = 'radio';
            radio.name = groupName;
            radio.className = "imgui-radiobutton";
            radio.value = value;
            radio.checked = value === defaultValue;
            radio.style.cssText = `
                margin-right: 8px;
                accent-color: var(--imgui-accent);
                width: 16px;
                height: 16px;
            `;
            
            const label = this._compatCreateElement('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 this._registerControl(radioButtons, id);
    }

    Image(src, alt = '', width = 'auto', height = 'auto', tooltip = '', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            justify-content: center;
            margin-bottom: var(--imgui-bottom-padding);
        `;
        
        const img = this._compatCreateElement('img');
        img.id = ImmediateGUI._generateId("ctrl_");
        img.className = 'imgui-image';
        img.src = src;
        img.alt = alt;
        img.className = "imgui-image imgui-control";
        if (tooltip) img.title = tooltip;
        
        img.style.cssText = `
            max-width: 100%;
            width: ${width};
            height: ${height};
            max-width: ${this.options.width}px;
            border-radius: 4px;
            border: 1px solid var(--imgui-border);
        `;
        
        wrapper.appendChild(img);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        return this._registerControl(img, id);
    }

    ListBox(items = [], defaultSelected = null, tooltip = '', onChange = null, itemType = 'text', id = null) {
        const wrapper = this._compatCreateElement('div');
        wrapper.className = "imgui-control imgui-wrapper";
        wrapper.style.cssText = `
            display: flex;
            flex-direction: column;
            margin-bottom: var(--imgui-bottom-padding);
        `;

        const listBox = this._compatCreateElement('ul');
        listBox.id = ImmediateGUI._generateId("ctrl_");
        listBox.className = "imgui-listbox imgui-control";
        listBox.style.cssText = `
            list-style: none;
            padding: 0;
            margin: 0;
            max-height: 160px;
            overflow-y: auto;
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            background: var(--imgui-input-bg);
            color: var(--imgui-text);
            font-family: inherit;
        `;
        if (tooltip && typeof tooltip === 'string' && tooltip.length > 0) listBox.title = tooltip;

        // Store items internally to keep track of the actual data
        let internalItems = [...items];
        let selectedIdx = -1;

        // Helper function to create a list item element
        const createListItem = (item, idx) => {
            const li = this._compatCreateElement('li');
            li.className = "imgui-listbox-item";
            li.style.cssText = `
                padding: 4px 12px;
                cursor: pointer;
                ${(itemType === 'text' ? 'transition: background 0.2s;' : '')}
                display: flex;
                align-items: center;
            `;

            let child;
            if (typeof itemType === 'function') {
                // Custom render function
                child = itemType(item, idx);
                li.appendChild(child);
            } else if (itemType === 'checkbox') {
                child = this.Checkbox('', item.checked || false);
                child.parentElement.querySelector('label')?.remove();

                const label = this._compatCreateElement('span');
                label.textContent = item.label || item;
                label.style.marginLeft = '8px';
                
                li.appendChild(child);
                li.appendChild(label);

                // Add change event for checkbox
                child.addEventListener('change', () => {
                    if (typeof onChange === 'function') {
                        onChange(item, idx, child.checked);
                    }
                });
            } else {
                // Default: text
                child = this._compatCreateElement('span');
                child.textContent = item.label || item;
                li.appendChild(child);
            }

            // Store item data reference on the li element
            li.itemData = item;
            li.itemIndex = idx;

            // Add interaction events for text items
            if (itemType === 'text') {
                li.addEventListener('click', () => {
                    // Clear all selections
                    Array.from(listBox.children).forEach(child => {
                        child.style.background = '';
                        child.style.color = 'var(--imgui-text)';
                    });
                    
                    // Highlight this item
                    li.style.background = 'var(--imgui-accent)';
                    li.style.color = '#fff';
                    selectedIdx = idx;
                    
                    if (typeof onChange === 'function') {
                        onChange(item, idx, li);
                    }
                });

                li.addEventListener('mouseenter', () => {
                    if (selectedIdx !== idx) {
                        li.style.background = 'var(--imgui-button-hover)';
                    }
                });

                li.addEventListener('mouseout', () => {
                    if (selectedIdx !== idx) {
                        li.style.background = '';
                    }
                });
            } else if (itemType === 'checkbox') {
                // Add hover effects for checkbox items
                li.addEventListener('mouseenter', () => {
                    li.style.background = 'var(--imgui-button-hover)';
                });

                li.addEventListener('mouseout', () => {
                    li.style.background = '';
                });
            }

            return li;
        };

        // Helper function to update all item indices
        const updateItemIndices = () => {
            Array.from(listBox.children).forEach((li, idx) => {
                li.itemIndex = idx;
            });
        };

        // Helper function to show placeholder when empty
        const updatePlaceholder = () => {
            const placeholder = listBox.querySelector('.imgui-listbox-placeholder');
            
            if (listBox.children.length === 0 || (listBox.children.length === 1 && placeholder)) {
                if (!placeholder) {
                    const placeholderLi = this._compatCreateElement('li');
                    placeholderLi.className = "imgui-listbox-placeholder";
                    placeholderLi.style.cssText = `
                        padding: 4px 12px;
                        color: var(--imgui-text);
                        opacity: 0.5;
                        font-style: italic;
                        text-align: center;
                    `;
                    placeholderLi.textContent = 'No items available';
                    listBox.appendChild(placeholderLi);
                }
            } else if (placeholder) {
                placeholder.remove();
            }
        };

        // Initialize list with items
        if (items.length > 0) {
            items.forEach((item, idx) => {
                const li = createListItem(item, idx);

                // Highlight selected item
                if (item === defaultSelected || idx === defaultSelected) {
                    if (itemType === 'text') {
                        li.style.background = 'var(--imgui-accent)';
                        li.style.color = '#fff';
                        selectedIdx = idx;
                    }
                }

                listBox.appendChild(li);
            });
        }
        else {
            updatePlaceholder();
        }

        // API Methods
        listBox.getSelected = () => {
            if (itemType === 'text') {
                if (selectedIdx >= 0 && selectedIdx < internalItems.length) {
                    return { item: internalItems[selectedIdx], index: selectedIdx };
                }
                return null;
            } else if (itemType === 'checkbox') {
                // For checkbox mode, return all checked items
                const checked = [];
                Array.from(listBox.children).forEach((li, idx) => {
                    if (!li.classList.contains('imgui-listbox-placeholder')) {
                        const checkbox = li.querySelector('input[type="checkbox"]');
                        if (checkbox && checkbox.checked) {
                            checked.push({ item: internalItems[idx], index: idx });
                        }
                    }
                });
                return checked;
            }
            return null;
        };

        listBox.setSelected = (idxOrItem) => {
            if (itemType !== 'text') {
                console.warn('ImmediateGUI: setSelected only works with text-type ListBox');
                return;
            }

            let idx = typeof idxOrItem === 'number' ? idxOrItem : internalItems.indexOf(idxOrItem);
            if (idx < 0 || idx >= internalItems.length) return;

            Array.from(listBox.children).forEach((child, i) => {
                if (!child.classList.contains('imgui-listbox-placeholder')) {
                    child.style.background = '';
                    child.style.color = 'var(--imgui-text)';
                    if (i === idx) {
                        child.style.background = 'var(--imgui-accent)';
                        child.style.color = '#fff';
                        child.scrollIntoView({ block: "nearest" });
                    }
                }
            });
            selectedIdx = idx;
        };

        listBox.addItem = (item, context = null, overrideOnChange = null) => {
            const idx = internalItems.length;
            internalItems.push(item);

            const li = createListItem(item, idx);
            
            // Remove placeholder if it exists
            const placeholder = listBox.querySelector('.imgui-listbox-placeholder');
            if (placeholder) placeholder.remove();

            if (context) li.context = context;
            if (overrideOnChange && typeof overrideOnChange === 'function') {
                // FIXME: overrideOnChange only works for text itemType for now
                if (itemType === 'text') {
                    li.addEventListener('click', (e) => {
                        e.preventDefault();
                        e.stopImmediatePropagation();

                        // Clear all selections
                        Array.from(listBox.children).forEach(child => {
                            child.style.background = '';
                            child.style.color = 'var(--imgui-text)';
                        });
                        // Highlight this item
                        li.style.background = 'var(--imgui-accent)';
                        li.style.color = '#fff';
                        selectedIdx = idx;
                        overrideOnChange(item, idx, li);
                    }, { capture: true});
                }
            }

            listBox.appendChild(li);
            updateItemIndices();
            
            return li;
        };

        listBox.removeItem = (idxOrItem) => {
            let idx;
            
            if (typeof idxOrItem === 'number') {
                idx = idxOrItem;
            } else {
                // Find the item in our internal items array
                idx = internalItems.findIndex(internalItem => {
                    if (typeof itemType === 'checkbox') {
                        return internalItem.label === idxOrItem.label;
                    }
                    return internalItem === idxOrItem;
                });
            }

            if (idx < 0 || idx >= listBox.children.length) {
                console.warn('ImmediateGUI: Invalid item index or item not found');
                return;
            }

            // Remove from internal items array
            internalItems.splice(idx, 1);

            // Remove from DOM
            const children = Array.from(listBox.children).filter(
                child => !child.classList.contains('imgui-listbox-placeholder')
            );
            
            if (children[idx]) {
                listBox.removeChild(children[idx]);
            }

            // Update selected index if needed
            if (selectedIdx === idx) {
                selectedIdx = -1;
            } else if (selectedIdx > idx) {
                selectedIdx--;
            }

            updateItemIndices();
            updatePlaceholder();
        };

        listBox.getItems = () => {
            return [...internalItems];
        };

        listBox.clear = () => {
            internalItems = [];
            selectedIdx = -1;
            
            // Remove all items except placeholder
            Array.from(listBox.children).forEach(child => {
                if (!child.classList.contains('imgui-listbox-placeholder')) {
                    listBox.removeChild(child);
                }
            });
            
            updatePlaceholder();
        };

        listBox.getItemCount = () => {
            return internalItems.length;
        };

        wrapper.appendChild(listBox);
        this._applyIndentation(wrapper);
        this._getTargetContainer().appendChild(wrapper);

        if (selectedIdx >= 0 && listBox.children[selectedIdx]) {
            requestAnimationFrame(() => {
                listBox.children[selectedIdx].scrollIntoView({ block: "nearest" });
            });
        }

        return this._registerControl(listBox, id);
    }

    _ControlFromNakedControlElement(Element, id = null) {
        const ElementParent = Element.parentElement;
        let ElementWrapper = Element;
        if (!ElementParent.classList.contains('imgui-wrapper')) {
            if (Element.className.includes('imgui-')) {
                // Wrap the element in a wrapper div
                ElementWrapper = this._compatCreateElement('div');
                ElementWrapper.className = "imgui-control imgui-wrapper";
                ElementWrapper.style.cssText = `display: flex; align-items: center; margin-bottom: var(--imgui-bottom-padding);`;
                ElementWrapper.appendChild(Element);
                debugger;
            }
            else {
                debugger;
            }
        }

        this._applyIndentation(ElementWrapper);
        this._getTargetContainer().appendChild(ElementWrapper);

        return this._registerControl(ElementWrapper, id);
    }

    Show() { 
        // FIXME: should we check if document.body is present before doing the Show logic?

        // NOTE: Clean up any empty wrappers left behind by removed controls, this was added after the introduction of the 'Listbox' function
        const wrappers = this.container.querySelectorAll('.imgui-wrapper');
        wrappers.forEach(wrapper => { 
            if (wrapper.children.length === 0) wrapper.remove();
        });

        if (this.container.style.display === 'none') {
            this.container.style.display = 'block';
            return this;
        } else {
            // NOTE: Only display gui layout warnings in non-debug mode
            if (this.indentationLevel > 0 && !this._guiDebugMode) {
                console.warn("ImmediateGUI: Show() called while in indentation mode. Did you forget to call EndIndentation() somewhere?");
            }

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

            if (this.currentRow && !this._guiDebugMode) {
                console.warn("ImmediateGUI: Show() called while in row mode. Did you forget to call EndRow() somewhere?");
            }

            if (this._isInToolbarMode === true && !this._guiDebugMode) {
                console.warn("ImmediateGUI: Show() called while in toolbar mode. Did you forget to call EndToolbarEntry() 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
                if (this._getControls().length > 0) {
                    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 = {}) {
        return ImmediateGUI.ShowModal(message, title, options);
    }
    static ShowModal(message, title = '', options = {}) {
        if (!ImmediateGUI.HasStaticStyles) {
            ImmediateGUI._applyGlobalStyles(null /* so we know it was called static */);
        }

        // Default options
        const config = {
            title: title || '',
            type: 'info', // 'info', 'warning', 'error', 'success'
            buttons: ['OK'],
            closeOnBackdropClick: true,
            width: 400,
            ...options
        };
        
        const backdrop = this._compatCreateElement('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.75);
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
            opacity: 0;
            transition: opacity 0.2s ease;
        `;

        const modal = this._compatCreateElement('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 = this._compatCreateElement('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 = this._compatCreateElement('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 = this._compatCreateElement('div');
            iconElement.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML(iconHtml);
            messageContainer.appendChild(iconElement);
        }
        
        const messageText = this._compatCreateElement('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 = this._compatCreateElement('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 = this._compatCreateElement('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 = 'var(--imgui-text)';
                button.style.borderColor = 'var(--imgui-border)';
                button.style.fontWeight = 'bold';

                button.addEventListener('mouseenter', () => { button.style.background = 'var(--imgui-button-hover)'; });
                button.addEventListener('mouseout', () => { button.style.background = 'var(--imgui-accent)'; });
            }
            else {
                button.className = 'imgui-button';

                button.style.background = 'var(--imgui-button-bg)';
                button.style.color = 'var(--imgui-text)';
                button.style.borderColor = 'var(--imgui-border)';
            }
            
            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
        };
    }

    ShowPrompt(message, title = '', defaultValue = '', placeholder = '', options = {}) {
        return ImmediateGUI.ShowPrompt(message, title, defaultValue, placeholder, options);
    }
    static ShowPrompt(message, title = '', defaultValue = '', placeholder = '', options = {}) {
        if (!ImmediateGUI.HasStaticStyles) {
            ImmediateGUI._applyGlobalStyles(null /* so we know it was called static */);
        }

        const config = {
            title: title || 'Input',
            placeholder: placeholder || '',
            width: 400,
            closeOnBackdropClick: true,
            ...options
        };
        
        return new Promise((resolve) => {
            const backdrop = this._compatCreateElement('div');
            backdrop.className = 'imgui-prompt-backdrop';
            backdrop.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0, 0, 0, 0.75);
                z-index: 10000;
                display: flex;
                align-items: center;
                justify-content: center;
                opacity: 0;
                transition: opacity 0.2s ease;
            `;

            const prompt = this._compatCreateElement('div');
            prompt.className = 'imgui-prompt';
            prompt.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;
                padding: 16px;
                transform: translateY(-20px);
                opacity: 0;
                transition: transform 0.3s ease, opacity 0.3s ease;
                font-family: inherit;
            `;
            
            // Title
            const titleEl = this._compatCreateElement('div');
            titleEl.className = 'imgui-prompt-title';
            titleEl.textContent = config.title;
            titleEl.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);
            `;
            prompt.appendChild(titleEl);
            
            // Message
            if (message) {
                const messageEl = this._compatCreateElement('div');
                messageEl.className = 'imgui-prompt-message';
                messageEl.textContent = message;
                messageEl.style.cssText = `
                    color: var(--imgui-text);
                    margin-bottom: 12px;
                    line-height: 1.5;
                `;
                prompt.appendChild(messageEl);
            }
            
            // Input field
            const input = this._compatCreateElement('input');
            input.type = 'text';
            input.value = defaultValue;
            input.placeholder = config.placeholder;
            input.className = 'imgui-input';
            input.style.cssText = `
                background: var(--imgui-input-bg);
                color: var(--imgui-text);
                border: 1px solid var(--imgui-border);
                border-radius: 4px;
                padding: 8px 10px;
                font-size: 14px;
                font-family: inherit !important;
                width: 100%;
                box-sizing: border-box;
                outline: none;
                transition: border-color 0.2s ease;
                font-family: inherit;
                margin-bottom: 16px;
            `;
            
            input.addEventListener('focus', () => {
                input.style.borderColor = 'var(--imgui-accent)';
            });
            
            input.addEventListener('blur', () => {
                input.style.borderColor = 'var(--imgui-border)';
            });
            
            prompt.appendChild(input);
            
            // Buttons
            const buttonsContainer = this._compatCreateElement('div');
            buttonsContainer.className = 'imgui-prompt-buttons';
            buttonsContainer.style.cssText = `
                display: flex;
                justify-content: flex-end;
                gap: 8px;
            `;
            
            const closePrompt = (value) => {
                prompt.style.transform = 'translateY(-20px)';
                prompt.style.opacity = '0';
                backdrop.style.opacity = '0';
                
                setTimeout(() => {
                    document.body.removeChild(backdrop);
                }, 300);
                
                resolve(value);
            };
            
            // Cancel button
            const cancelBtn = this._compatCreateElement('button');
            cancelBtn.textContent = 'Cancel';
            cancelBtn.className = 'imgui-button';
            cancelBtn.style.background = 'var(--imgui-accent)';
            cancelBtn.style.color = 'var(--imgui-text)';
            cancelBtn.style.borderColor = 'var(--imgui-border)';
            cancelBtn.addEventListener('click', () => closePrompt(null));
            buttonsContainer.appendChild(cancelBtn);
            
            // OK button
            const okBtn = this._compatCreateElement('button');
            okBtn.textContent = 'OK';
            okBtn.className = 'imgui-button imgui-primary-button';
            okBtn.style.background = 'var(--imgui-accent)';
            okBtn.style.color = 'var(--imgui-text)';
            okBtn.style.borderColor = 'var(--imgui-border)';
            okBtn.style.fontWeight = 'bold';
            
            okBtn.addEventListener('mouseenter', () => { 
                okBtn.style.background = 'var(--imgui-button-hover)'; 
            });
            okBtn.addEventListener('mouseout', () => { 
                okBtn.style.background = 'var(--imgui-accent)'; 
            });
            okBtn.addEventListener('click', () => closePrompt(input.value));
            buttonsContainer.appendChild(okBtn);
            
            prompt.appendChild(buttonsContainer);
            backdrop.appendChild(prompt);
            
            // Handle backdrop click
            if (config.closeOnBackdropClick) {
                backdrop.addEventListener('click', (e) => {
                    if (e.target === backdrop) {
                        closePrompt(null);
                    }
                });
            }
            
            // Handle Enter key
            input.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault();
                    closePrompt(input.value);
                } else if (e.key === 'Escape') {
                    e.preventDefault();
                    closePrompt(null);
                }
            });
            
            // Show the prompt
            document.body.appendChild(backdrop);
            
            setTimeout(() => {
                backdrop.style.opacity = '1';
                prompt.style.transform = 'translateY(0)';
                prompt.style.opacity = '1';
                input.focus();
                input.select();
            }, 10);
        });
    }

    ShowToast(message, type = 'info', duration = 3000, position = 'top-right') {
        return ImmediateGUI.ShowToast(message, type, duration, position);
    }
    static ShowToast(message, type = 'info', duration = 3000, position = 'top-right') {
        if (!ImmediateGUI.HasStaticStyles) {
            ImmediateGUI._applyGlobalStyles(null /* so we know it was called static */);
        }

        // Create toast container if it doesn't exist
        const idFriendlyPositionName = position.replace('-', '_');
        let positionAppropriateElementName = `imgui-toast-container-${idFriendlyPositionName}`;

        const debugForceTopRight = false;

        if (debugForceTopRight) {
            position = 'top-right';
            positionAppropriateElementName = `imgui-toast-container-top_right`;
        }

        let toastContainer = document.getElementById(positionAppropriateElementName);
        if (!toastContainer) {
            toastContainer = this._compatCreateElement('div');
            toastContainer.id = positionAppropriateElementName;
            toastContainer.style.cssText = `
                position: fixed;
                /* top: 20px; */
                /* right: 20px; */
                z-index: 10001;
                display: flex;
                flex-direction: column;
                gap: 10px;
                pointer-events: none;
            `;
            
            switch (position) {
                case 'top-left':
                    toastContainer.style.top = '20px';
                    toastContainer.style.left = '20px';
                    break;
                case 'top-right':
                    toastContainer.style.top = '20px';
                    toastContainer.style.right = '20px';
                    break;
                case 'bottom-left':
                    toastContainer.style.bottom = '20px';
                    toastContainer.style.left = '20px';
                    break;
                case 'bottom-right':
                    toastContainer.style.bottom = '20px';
                    toastContainer.style.right = '20px';
                    break;
                default:
                    // Default to top-right if invalid position is given
                    position = 'top-right';
                    positionAppropriateElementName = `imgui-toast-container-top_right`;
                    toastContainer.style.top = '20px';
                    toastContainer.style.right = '20px';
                    break;
            }

            (document.body || document.documentElement).appendChild(toastContainer);
            //document.body.appendChild(toastContainer);
        }

        // Create toast element
        // FIXME: something is causing inconsitent behavior with the toast sliding IN animation
        // sometimes it doesnt animate properly (just pops in) and sometimes it does

        // FIXME: also, when multiple toasts are shown in succession, the animation gets messed up
        // as in the first toast animates in properly, but the subsequent toasts just pop in without animation

        // FIXME: to test multiple toasts, uncomment the code below
        // [
        //     'top-left',
        //     'bottom-left',
        //     'top-right',
        //     'bottom-right',
        // ].forEach(toastPosition => {
        //     const lowerBound = 2;
        //     const upperBound = 5;
        //     const randomAmount = upperBound <= 1 ? 1 : Math.floor(Math.random() * upperBound) + lowerBound; /* <lower bound> to <upper bound> */
        //     const showTimeMs = 2500;
        //     const delayBetweenToastsMs = 500;
        //     for (let i = 0; i < randomAmount; i++) {
        //         const randomType = ['success','error','warning','info'][Math.floor(Math.random() * 4)];

        //         if (delayBetweenToastsMs > 0) {
        //             new Promise((resolve) => {
        //                 setTimeout(() => {
        //                     resolve();
        //                 }, i * delayBetweenToastsMs);

        //             }).then(() => {
        //                 ImmediateGUI.ShowToast(`(${(i + 1)}/${randomAmount}) This is toast message of type ${randomType}. Bla bla bla bla.`, randomType, showTimeMs, toastPosition);
        //             })
        //         }
        //         else {
        //             ImmediateGUI.ShowToast(`(${(i + 1)}/${randomAmount}) This is toast message of type ${randomType}. Bla bla bla bla.`, randomType, showTimeMs, toastPosition);
        //         }
        //     }
        // });

        const toast = this._compatCreateElement('div');
        toast.className = 'imgui-toast';
        toast.style.cssText = `
            background: var(--imgui-bg);
            color: var(--imgui-text);
            border: 1px solid var(--imgui-border);
            border-radius: 4px;
            padding: 12px 16px;
            min-width: 250px;
            max-width: 400px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 10px;
            pointer-events: auto;
            opacity: 0;
            transition: all 0.3s ease;
        `;

        // Ensure they animate in from the correct side
        const enterTransform = position.includes('left') 
            ? `translateX(-100%)` 
            : `translateX(100%)`;

        toast.style.transform = enterTransform;

        // Add type-specific styling and icon
        let icon = '';
        let accentColor = 'var(--imgui-accent)';
        
        switch(type) {
            case 'success':
                icon = '✅';
                accentColor = '#5cb85c';
                break;
            case 'error':
                icon = '❌';
                accentColor = '#d9534f';
                break;
            case 'warning':
                icon = '⚠️';
                accentColor = '#f0ad4e';
                break;
            case 'info':
            default:
                icon = 'ℹ️';
                accentColor = 'var(--imgui-accent)';
                break;
        }

        toast.style.borderLeftWidth = '4px';
        toast.style.borderLeftColor = accentColor;

        // Create icon element
        const iconElement = this._compatCreateElement('div');
        iconElement.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML(icon);
        iconElement.style.cssText = `
            font-size: 20px;
            line-height: 1;
            flex-shrink: 0;
        `;

        // Create message element
        const messageElement = this._compatCreateElement('div');
        messageElement.style.cssText = `
            flex: 1;
            line-height: 1.4;
            word-wrap: break-word;
        `;
        
        if (typeof message === 'object' && message.nodeType) {
            messageElement.appendChild(message);
        } else {
            messageElement.textContent = message;
        }

        // Create close button
        const closeButton = this._compatCreateElement('button');
        closeButton.innerHTML = ImmediateGUI._escapeHTMLPolicy.createHTML('×');
        closeButton.style.cssText = `
            background: none;
            border: none;
            color: var(--imgui-text);
            font-size: 20px;
            line-height: 1;
            cursor: pointer;
            padding: 0;
            /* margin-left: 4px; */
            opacity: 0.6;
            transition: opacity 0.2s ease;
            flex-shrink: 0;
        `;

        closeButton.addEventListener('mouseenter', () => {
            closeButton.style.opacity = '1';
        });

        closeButton.addEventListener('mouseleave', () => {
            closeButton.style.opacity = '0.6';
        });

        const removeToast = () => {
            if (!toast.parentElement) return;

            // Determine the correct exit direction based on position
            // Left-side toasts should exit left (-100%), right-side should exit right (100%)
            const exitTransform = position.includes('left') 
                ? `translateX(-100%)`
                : `translateX(100%)`;

            toast.style.transform = exitTransform;
            toast.style.opacity = '0';
            
            setTimeout(() => {
                if (toast.parentElement) {
                    toast.parentElement.removeChild(toast);
                }
                
                // Remove container if empty
                if (toastContainer.children.length === 0) {
                    toastContainer.remove();
                }
            }, 300);
        };

        toast.removeToast = removeToast;
        closeButton.addEventListener('click', removeToast);

        // Assemble toast
        // TODO: if position is left, reverse order of icon and message and close button
        // closeButton should always be closest to the nearest edge
        if (position.includes('left')) {
            closeButton.style.marginLeft = '0px';
            closeButton.style.marginRight = '4px';

            //messageElement.style.textAlign = 'right';
            //toast.appendChild(closeButton);
            //toast.appendChild(messageElement);
            //toast.appendChild(iconElement);
        }
        else {
            closeButton.style.marginLeft = '4px';
            closeButton.style.marginRight = '0px';

            //toast.appendChild(iconElement);
            //toast.appendChild(messageElement);
            //toast.appendChild(closeButton);
        }

        toast.appendChild(iconElement);
        toast.appendChild(messageElement);
        toast.appendChild(closeButton);

        // Add to container
        // TODO: if document.body is present, migrate the toast container from document.documentElement to document.body?
        toastContainer.appendChild(toast);

        // Animate in
        requestAnimationFrame(() => {
            toast.style.opacity = '1';
            toast.style.transform = 'translateX(0)';
        });

        // Auto-remove after duration (if duration > 0)
        if (duration > 0) {
            setTimeout(removeToast, duration);
        }

        // TODO: Should toast objects have custom ids and be registered like other controls?
        return toast;
    }

    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;
    
        // Handle both regular buttons and toolbar buttons
        this.container.querySelectorAll('.imgui-button').forEach(el => {
            const isToolbarButton = this.toolbar && (['imgui-toolbar-button','imgui-toolbar-entry-button'].some(cls => el.classList.contains(cls)));
            el.style.background = isToolbarButton ? this.theme.sectionBg : 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;
        });

        // TODO: Handle toolbar checkboxes separately
        // this.container.querySelectorAll('.imgui-toolbar-checkbox-wrapper').forEach(el => {
        //     el.style.color = this.theme.text;
        //     el.style.borderColor = this.theme.border;
        //     el.style.background = this.theme.sectionBg;
        // })

        // TODO: Handle both normal separators and toolbar separators

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

        this.container.querySelectorAll('.imgui-toggle-input').forEach(toggle => {
            const track = toggle.track;
            const thumb = toggle.thumb;
            
            if (track && thumb) {
                // Update track color based on state
                if (toggle.checked) {
                    track.style.background = 'var(--imgui-accent)';
                } else {
                    track.style.background = 'var(--imgui-border)';
                }
                
                // Update thumb color
                thumb.style.background = 'var(--imgui-text)';
                
                // Update border
                track.style.borderColor = 'var(--imgui-border)';
            }
        });

        // FIXME: Apply the theme to the toolbar controls, including the dropdown menus
        // if (this.toolbar) {
        //     Array.from(this.toolbar.children).forEach(control => {
        //         const isDropdown = control.classList.contains('imgui-toolbar-entry');
                
        //         if (isDropdown) {
        //             // NOTE: Dropdown parent gets the default styling
        //             control.style.background = this.theme.sectionBg;
        //             control.style.color = this.theme.text;
        //             control.style.borderColor = this.theme.border;

        //             // NOTE: control was built using [Begin/End]ToolbarEntry functions
        //             // Itterate over its children
        //             const children = Array.from(control.querySelectorAll('*'));
        //             debugger;
        //         }
        //         else {
        //             // NOTE: Control was built using the Toolbar* functions
        //             control.style.background = this.theme.sectionBg;
        //             control.style.color = this.theme.text;
        //             control.style.borderColor = this.theme.border;
        //         }
        //     });
        // }

        return this;
    }

    // TODO: how do we prevent third parties that have a reference to an ImmediateGUI instance from calling this function?
    _compatCreateElement(tagName, options = undefined, keepInDom = false) {
        return ImmediateGUI._compatCreateElement(tagName, options, keepInDom);
    }
    static _cachedHasGMAddElement = null;
    static _compatCreateElement(tagName, options = undefined, keepInDom = false) {
        if (ImmediateGUI._cachedHasGMAddElement === null) {
            ImmediateGUI._cachedHasGMAddElement = typeof GM_addElement === 'function';
        }

        const hasGmAddElement = ImmediateGUI._cachedHasGMAddElement;
        if (hasGmAddElement === false) {
            // TODO: check if options is an object and only has the key 'is'
            // if so, pass it to createElement, otherwise ignore options
            // const isValidOptions = options && typeof options === 'object' && Object.keys(options).length === 1 && options.is;
            // if (isValidOptions) options = { is: options.is };
            // else options = undefined;

            const element = document.createElement(tagName, options, /* force */ true);
            return element;
        }
        else {
            // TODO: for compability, override appendChild, insertBefore and related functions of Node.prototype
            // to check if is already added to the DOM (side effect of using GM_addElement) and do nothing if it is

            try {
                const element = GM_addElement(tagName, options);
                if (!keepInDom && element.isConnected === true) {
                    element.remove();
                }

                // NOTE: we shouldnt strip id if options is an object and has a property 'id' that is not empty
                if (element.id && (!options || !options.id)) element.removeAttribute('id');

                return element;
            } catch (e) {
                //console.warn('GM_addElement failed, falling back to document.createElement. This may cause issues with certain control types. Error:', e);
                debugger;

                // TODO: check if options is an object and only has the key 'is'
                // if so, pass it to createElement, otherwise ignore options
                return document.createElement(tagName, options, /* force */ true);
            }
        }
    }
}