// ComfyUI Endless 🌊✨ Node Spawner - Optimized Version

(function waitForHelpers() {
    if (typeof window.EndlessHelpers === 'undefined') {
        console.warn("⏳ Waiting for EndlessHelpers to be ready...");
        setTimeout(waitForHelpers, 100);
        return;
    }

    const {
        registerEndlessTool,
        onThemeChange,
        getComfyUIColors,
        toRGBA,
        makeDraggable,
        addButtonHoverEffects
    } = window.EndlessHelpers;

    console.log("✅ Endless Node Spawner loaded.");

    // State management
    let currentDialog = null;
    let unregisterThemeCallback = null;
    let allNodesData = [];
    let currentFilter = '';
    let searchTimeout = null;
    let hoverTimeout = null;

    // Persistent data
    let recentlyUsedNodes = JSON.parse(localStorage.getItem('endlessNodeLoader_recentlyUsed') || '[]');
    let searchHistory = JSON.parse(localStorage.getItem('endlessNodeLoader_searchHistory') || '[]');

    // Constants
    const MAX_RECENT = 15;
    const MAX_HISTORY = 15;
    const DEFAULT_SPACING = { x: 300, y: 150 };
    const NODE_PADDING = 20;

    function createStyleCSS(colors) {
        return `
            .dialog-container {
                display: flex;
                flex-direction: column;
                height: 60vh;
                width: 35vw;
                min-width: 400px;
                min-height: 300px;
                background: ${colors.menu};
                color: ${colors.inputText};
                padding: 10px;
                border: 1px solid ${colors.border};
                border-radius: 8px;
                box-shadow: ${colors.shadow || '0 4px 20px rgba(0,0,0,0.5)'};
                z-index: 9999;
                overflow: hidden;
                box-sizing: border-box;
            }
            .dialog-title {
                margin: 0 0 15px 0;
                cursor: move;
                user-select: none;
                padding: 6px;
                background: ${colors.menuSecondary};
                color: ${colors.inputText};
                border-radius: 4px;
                font-size: 14px;
                border-bottom: 1px solid ${colors.border};
            }
            .filter-section {
                flex: 0 0 auto;
                display: flex;
                flex-direction: column;
                gap: 8px;
                margin-bottom: 8px;
                padding-bottom: 8px;
                border-bottom: 1px solid ${colors.border};
            }
            .filter-row {
                position: relative;
                display: flex;
                gap: 8px;
                align-items: center;
            }
            .filter-input {
                flex: 1;
                background: ${colors.inputBg};
                color: ${colors.inputText};
                border: 1px solid ${colors.border};
                border-radius: 4px;
                padding: 6px 8px;
                font-size: 12px;
            }
            .filter-input:focus {
                outline: none;
                border-color: ${colors.accent};
            }
            .expand-btn {
                background: ${colors.inputBg};
                color: ${colors.inputText};
                border: 1px solid ${colors.border};
                border-radius: 4px;
                padding: 6px 12px;
                font-size: 12px;
                cursor: pointer;
            }
            .expand-btn:hover {
                background: ${colors.hoverBg};
                border-color: ${colors.accent};
            }
            .search-dropdown {
                position: absolute;
                top: 100%;
                left: 0;
                right: 80px;
                background: ${colors.menu};
                border: 1px solid ${colors.border};
                border-radius: 4px;
                max-height: 150px;
                overflow-y: auto;
                z-index: 10000;
                display: none;
            }
            .search-item {
                padding: 6px 8px;
                cursor: pointer;
                font-size: 12px;
                border-bottom: 1px solid ${colors.border};
            }
            .search-item:last-child { border-bottom: none; }
            .search-item:hover { background: ${colors.hoverBg}; }
            .counters {
                display: flex;
                justify-content: space-between;
                font-size: 11px;
                color: ${colors.descriptionText};
            }
            .counter-selected {
                color: ${colors.accent};
                font-weight: bold;
            }
            .recent-section {
                flex: 0 0 auto;
                display: flex;
                flex-wrap: wrap;
                gap: 6px;
                min-height: 30px;
                max-height: 15%;
                overflow-y: auto;
                border-bottom: 1px solid ${colors.border};
                padding-bottom: 6px;
                margin-bottom: 6px;
            }
            .recent-chip {
                background: ${toRGBA(colors.accent, 0.1)};
                color: ${colors.inputText};
                padding: 4px 8px;
                border-radius: 4px;
                font-size: 11px;
                cursor: pointer;
                border: 1px solid transparent;
                transition: all 0.2s ease;
            }
            .recent-chip:hover {
                border-color: ${colors.accent};
                background: ${toRGBA(colors.accent, 0.2)};
            }
            .node-list {
                flex: 1 1 auto;
                overflow-y: auto;
                border-bottom: 1px solid ${colors.border};
                padding-bottom: 6px;
                margin-bottom: 6px;
            }
            .category {
                margin-bottom: 4px;
            }
            .category > summary {
                cursor: pointer;
                display: flex;
                align-items: center;
                gap: 8px;
                padding: 4px 0;
                list-style: none;
                color: ${colors.inputText};
            }
            .category > summary::-webkit-details-marker { display: none; }
            .category > summary::before {
                content: "▶";
                width: 12px;
                text-align: center;
                color: ${colors.descriptionText};
                font-size: 10px;
                transition: transform 0.2s ease;
            }
            .category[open] > summary::before {
                transform: rotate(90deg);
                color: ${colors.inputText};
            }
            .category > summary:hover {
                background: ${colors.hoverBg};
                border-radius: 4px;
            }
            .category ul {
                margin: 4px 0;
                padding-left: 1em;
            }
            .category li:hover {
                background: ${colors.hoverBg};
                border-radius: 4px;
            }
            .category input[type="checkbox"] {
                accent-color: ${colors.accent};
            }
            .cat-btn {
                background: ${colors.inputBg};
                color: ${colors.inputText};
                border: 1px solid ${colors.border};
                font-size: 10px;
                padding: 2px 6px;
                border-radius: 3px;
                cursor: pointer;
                transition: all 0.2s ease;
            }
            .cat-btn:hover {
                background: ${colors.hoverBg};
                border-color: ${colors.accent};
            }
            .cat-btn.select { background: ${toRGBA('#4CAF50', 0.1)}; }
            .cat-btn.deselect { background: ${toRGBA('#f44336', 0.1)}; }
            .footer {
                display: flex;
                justify-content: space-between;
                gap: 8px;
            }
            .btn-group {
                display: flex;
                gap: 8px;
            }
            .dialog-btn {
                background: ${colors.inputBg};
                color: ${colors.inputText};
                border: 1px solid ${colors.border};
                border-radius: 4px;
                padding: 6px 12px;
                font-size: 12px;
                cursor: pointer;
                transition: all 0.2s ease;
            }
            .dialog-btn:hover {
                background: ${colors.hoverBg};
                border-color: ${colors.accent};
            }
            .dialog-btn.primary {
                background: ${toRGBA(colors.accent || '#4CAF50', 0.2)};
                border-color: ${colors.accent || '#4CAF50'};
            }
            .dialog-btn.secondary {
                background: ${toRGBA(colors.errorText || '#f44336', 0.2)};
                border-color: ${colors.errorText || '#f44336'};
            }
            .hidden { display: none !important; }
        `;
    }

    function updateTheme() {
        if (!currentDialog) return;
        
        const colors = getComfyUIColors();
        const styleTag = document.getElementById('node-loader-style');
        if (styleTag) {
            styleTag.textContent = createStyleCSS(colors);
        }
    }

    function getApp() {
        return window.app || window.comfyApp;
    }

    function getExistingNodePositions() {
        const app = getApp();
        const positions = [];
        
        if (app?.graph?.nodes) {
            app.graph.nodes.forEach(node => {
                if (node.pos) {
                    positions.push({
                        x: node.pos[0],
                        y: node.pos[1],
                        width: node.size?.[0] || 200,
                        height: node.size?.[1] || 100
                    });
                }
            });
        }
        return positions;
    }

    function findNonOverlappingPosition(startX, startY, width, height, existingPositions, spacingX, spacingY) {
        let x = startX;
        let y = startY;
        
        while (true) {
            let overlaps = false;
            
            for (const pos of existingPositions) {
                if (!(x + width + NODE_PADDING < pos.x || 
                      x - NODE_PADDING > pos.x + pos.width || 
                      y + height + NODE_PADDING < pos.y || 
                      y - NODE_PADDING > pos.y + pos.height)) {
                    overlaps = true;
                    break;
                }
            }
            
            if (!overlaps) return { x, y };
            
            x += spacingX;
            if (x > startX + spacingX * 5) {
                x = startX;
                y += spacingY;
            }
        }
    }

    function spawnNodes(types, spacingX = DEFAULT_SPACING.x, spacingY = DEFAULT_SPACING.y) {
        const app = getApp();
        if (!app?.graph?.add) {
            alert("ComfyUI graph not available.");
            return;
        }

        const startX = -app.canvas.ds.offset[0] + 50;
        const startY = -app.canvas.ds.offset[1] + 50;
        const existingPositions = getExistingNodePositions();
        
        const spawnedNodes = [];
        
        types.forEach((type, i) => {
            const node = LiteGraph.createNode(type);
            if (node) {
                const nodeWidth = node.size?.[0] || 200;
                const nodeHeight = node.size?.[1] || 100;
                
                const position = findNonOverlappingPosition(
                    startX + (i % 5) * spacingX,
                    startY + Math.floor(i / 5) * spacingY,
                    nodeWidth,
                    nodeHeight,
                    existingPositions,
                    spacingX,
                    spacingY
                );
                
                node.pos = [position.x, position.y];
                app.graph.add(node);
                spawnedNodes.push(type);
                
                existingPositions.push({
                    x: position.x,
                    y: position.y,
                    width: nodeWidth,
                    height: nodeHeight
                });
            } else {
                console.warn(`Could not create node: ${type}`);
            }
        });
        
        updateRecentlyUsedNodes(spawnedNodes);
        app.graph.setDirtyCanvas(true, true);
    }

    function updateRecentlyUsedNodes(newNodes) {
        newNodes.forEach(nodeType => {
            const index = recentlyUsedNodes.indexOf(nodeType);
            if (index > -1) recentlyUsedNodes.splice(index, 1);
            recentlyUsedNodes.unshift(nodeType);
        });
        
        recentlyUsedNodes = recentlyUsedNodes.slice(0, MAX_RECENT);
        localStorage.setItem('endlessNodeLoader_recentlyUsed', JSON.stringify(recentlyUsedNodes));
        
        if (currentDialog) updateRecentChips();
    }

    function updateRecentChips() {
        const recentSection = currentDialog.querySelector('.recent-section');
        if (!recentSection) return;
        
        recentSection.innerHTML = '';
        
        recentlyUsedNodes.forEach(nodeType => {
            const chip = document.createElement('button');
            chip.className = 'recent-chip';
            
            const nodeClass = LiteGraph.registered_node_types[nodeType];
            const displayName = nodeClass?.title || nodeClass?.name || nodeType.split("/").pop();
            chip.textContent = displayName;
            chip.title = nodeType;
            
            chip.onclick = () => {
                const checkbox = Array.from(currentDialog.querySelectorAll('.node-checkbox')).find(cb => {
                    return cb.closest('li').dataset.nodeType === nodeType;
                });
                if (checkbox) {
                    checkbox.checked = true;
                    updateSelectedCounter();
                }
            };
            
            recentSection.appendChild(chip);
        });
    }

    function addToSearchHistory(searchTerm) {
        if (!searchTerm.trim() || searchHistory.includes(searchTerm)) return;
        
        searchHistory.unshift(searchTerm);
        searchHistory = searchHistory.slice(0, MAX_HISTORY);
        localStorage.setItem('endlessNodeLoader_searchHistory', JSON.stringify(searchHistory));
    }

    function showSearchHistory(inputElement) {
        const dropdown = inputElement.parentElement.querySelector('.search-dropdown');
        if (!dropdown || searchHistory.length === 0) {
            if (dropdown) dropdown.style.display = 'none';
            return;
        }
        
        dropdown.innerHTML = '';
        searchHistory.forEach(term => {
            const item = document.createElement('div');
            item.className = 'search-item';
            item.textContent = term;
            item.onclick = () => {
                inputElement.value = term;
                applyFilter(term, true);
                hideSearchHistory(dropdown);
            };
            dropdown.appendChild(item);
        });
        
        dropdown.style.display = 'block';
        
        setTimeout(() => {
            if (dropdown.style.display === 'block') {
                hideSearchHistory(dropdown);
            }
        }, 10000);
    }

    function hideSearchHistory(dropdown) {
        dropdown.style.display = 'none';
    }

    function applyFilter(filterText, saveToHistory = true) {
        currentFilter = filterText.toLowerCase();
        const nodeList = currentDialog.querySelector('.node-list');
        
        if (!currentFilter) {
            nodeList.querySelectorAll('.category, .category li').forEach(el => {
                el.classList.remove('hidden');
            });
            updateTotalCounter();
            return;
        }
        
        nodeList.querySelectorAll('.category').forEach(details => {
            const categoryName = details.querySelector('summary span').textContent.toLowerCase();
            const categoryMatches = categoryName.includes(currentFilter);
            
            let hasMatchingNodes = false;
            const nodeItems = details.querySelectorAll('li');
            
            nodeItems.forEach(li => {
                const nodeText = li.textContent.toLowerCase();
                const nodeType = li.dataset.nodeType?.toLowerCase() || '';
                const matches = nodeText.includes(currentFilter) || nodeType.includes(currentFilter);
                
                if (matches) {
                    li.classList.remove('hidden');
                    hasMatchingNodes = true;
                } else {
                    li.classList.add('hidden');
                }
            });
            
            if (categoryMatches || hasMatchingNodes) {
                details.classList.remove('hidden');
                if (hasMatchingNodes && !categoryMatches) {
                    details.open = true;
                }
            } else {
                details.classList.add('hidden');
            }
        });
        
        updateTotalCounter();
    }

    function updateSelectedCounter() {
        const counter = currentDialog.querySelector('.counter-selected');
        if (!counter) return;
        
        const checkedBoxes = currentDialog.querySelectorAll('.node-checkbox:checked');
        counter.textContent = `Selected: ${checkedBoxes.length}`;
    }

    function updateTotalCounter() {
        const counter = currentDialog.querySelector('.counter-total');
        if (!counter) return;
        
        const visibleNodes = currentDialog.querySelectorAll('.category li:not(.hidden)');
        counter.textContent = `Total: ${visibleNodes.length}/${allNodesData.length}`;
    }

    function toggleAllCategories(expand) {
        const details = currentDialog.querySelectorAll('.category:not(.hidden)');
        details.forEach(detail => {
            detail.open = expand;
        });
    }

    function buildHierarchy(nodes) {
        const root = {};
        nodes.forEach(n => {
            let current = root;
            n.pathParts.forEach((part, idx) => {
                if (!current[part]) {
                    current[part] = { _nodes: [], _subcategories: {} };
                }
                if (idx === n.pathParts.length - 1) {
                    current[part]._nodes.push(n);
                } else {
                    current = current[part]._subcategories;
                }
            });
        });
        return root;
    }

    function countNodesInCategory(categoryObj) {
        let count = categoryObj._nodes?.length || 0;
        if (categoryObj._subcategories) {
            Object.values(categoryObj._subcategories).forEach(sub => {
                count += countNodesInCategory(sub);
            });
        }
        return count;
    }

    function selectAllInCategory(categoryDetails, select = true) {
        const checkboxes = categoryDetails.querySelectorAll("input[type='checkbox']");
        checkboxes.forEach(checkbox => {
            checkbox.checked = select;
        });
        updateSelectedCounter();
    }

    function renderCategory(categoryObj, depth = 0) {
        return Object.entries(categoryObj)
            .sort(([a], [b]) => a.localeCompare(b))
            .map(([cat, obj]) => {
                const totalNodes = countNodesInCategory(obj);

                const details = document.createElement("details");
                details.className = "category";
                details.style.paddingLeft = `${depth * 1.2}em`;

                const summary = document.createElement("summary");
                
                const categoryName = document.createElement("span");
                categoryName.textContent = `${cat} (${totalNodes})`;

                const selectAllBtn = document.createElement("button");
                selectAllBtn.textContent = "All";
                selectAllBtn.className = "cat-btn select";
                selectAllBtn.onclick = (e) => {
                    e.stopPropagation();
                    selectAllInCategory(details, true);
                };

                const selectNoneBtn = document.createElement("button");
                selectNoneBtn.textContent = "None";
                selectNoneBtn.className = "cat-btn deselect";
                selectNoneBtn.onclick = (e) => {
                    e.stopPropagation();
                    selectAllInCategory(details, false);
                };

                summary.appendChild(categoryName);
                summary.appendChild(selectAllBtn);
                summary.appendChild(selectNoneBtn);
                details.appendChild(summary);

                const list = document.createElement("ul");
                (obj._nodes || []).forEach(node => {
                    const li = document.createElement("li");
                    li.dataset.nodeType = node.type;
                    
                    const checkbox = document.createElement("input");
                    checkbox.type = "checkbox";
                    checkbox.className = "node-checkbox";
                    checkbox.onchange = updateSelectedCounter;
                    
                    const label = document.createElement("label");
                    label.textContent = node.displayName;

                    li.appendChild(checkbox);
                    li.appendChild(label);
                    list.appendChild(li);
                });

                details.appendChild(list);

                const subCategories = renderCategory(obj._subcategories || {}, depth + 1);
                subCategories.forEach(sub => details.appendChild(sub));

                return details;
            });
    }

    function getSelectedNodeTypes() {
        const selected = [];
        currentDialog.querySelectorAll('.node-checkbox:checked').forEach(checkbox => {
            const nodeType = checkbox.closest('li').dataset.nodeType;
            if (nodeType) selected.push(nodeType);
        });
        return selected;
    }

    function clearSelectedNodes() {
        currentDialog.querySelectorAll('.node-checkbox:checked').forEach(checkbox => {
            checkbox.checked = false;
        });
        updateSelectedCounter();
    }

    function setupEventHandlers() {
        const filterInput = currentDialog.querySelector('.filter-input');
        const searchDropdown = currentDialog.querySelector('.search-dropdown');
        const expandBtn = currentDialog.querySelector('.expand-btn');
        
        let isExpanded = false;

        // Filter input handlers
        filterInput.oninput = (e) => applyFilter(e.target.value, false);
        
        filterInput.onkeydown = (e) => {
            if (e.key === 'Enter' && e.target.value.trim()) {
                addToSearchHistory(e.target.value.trim());
                hideSearchHistory(searchDropdown);
            } else if (e.key === 'ArrowDown' && searchHistory.length > 0) {
                showSearchHistory(filterInput);
                e.preventDefault();
            }
        };

        filterInput.onfocus = (e) => {
            if (!e.target.value.trim()) {
                showSearchHistory(filterInput);
            }
        };

        filterInput.onblur = (e) => {
            if (e.target.value.trim()) {
                addToSearchHistory(e.target.value.trim());
            }
            if (hoverTimeout) {
                clearTimeout(hoverTimeout);
                hoverTimeout = null;
            }
            setTimeout(() => hideSearchHistory(searchDropdown), 150);
        };

        filterInput.onmouseenter = () => {
            if (searchTimeout) {
                clearTimeout(searchTimeout);
                searchTimeout = null;
            }
            
            hoverTimeout = setTimeout(() => {
                if (searchHistory.length > 0) {
                    showSearchHistory(filterInput);
                }
            }, 1000);
        };

        filterInput.onmouseleave = () => {
            if (hoverTimeout) {
                clearTimeout(hoverTimeout);
                hoverTimeout = null;
            }
            
            searchTimeout = setTimeout(() => {
                hideSearchHistory(searchDropdown);
            }, 10000);
        };

        // Expand button
        expandBtn.onclick = () => {
            isExpanded = !isExpanded;
            toggleAllCategories(isExpanded);
            expandBtn.textContent = isExpanded ? "Collapse All" : "Expand All";
        };

        // Button handlers
        currentDialog.querySelector('#spawn-btn').onclick = () => {
            const selectedTypes = getSelectedNodeTypes();
            if (selectedTypes.length === 0) {
                alert("Please select at least one node to spawn.");
                return;
            }
            spawnNodes(selectedTypes);
            closeDialog();
        };

        currentDialog.querySelector('#clear-btn').onclick = clearSelectedNodes;
        currentDialog.querySelector('#cancel-btn').onclick = closeDialog;
        currentDialog.querySelector('#clear-history-btn').onclick = () => {
            searchHistory = [];
            localStorage.setItem('endlessNodeLoader_searchHistory', JSON.stringify(searchHistory));
            const dropdown = currentDialog.querySelector('.search-dropdown');
            if (dropdown) dropdown.style.display = 'none';
        };
        currentDialog.querySelector('#clear-recent-btn').onclick = () => {
            recentlyUsedNodes = [];
            localStorage.setItem('endlessNodeLoader_recentlyUsed', JSON.stringify(recentlyUsedNodes));
            updateRecentChips(); // This will clear the chips
        };

        // ESC key handler
        const escHandler = (e) => e.key === 'Escape' && closeDialog();
        document.addEventListener('keydown', escHandler);
        
        return escHandler;
    }

    function createNodeLoaderDialog() {
        if (currentDialog) return;

        const colors = getComfyUIColors();

        // Clean up existing style
        document.getElementById('node-loader-style')?.remove();

        // Create style tag
        const style = document.createElement('style');
        style.id = 'node-loader-style';
        style.textContent = createStyleCSS(colors);
        document.head.appendChild(style);

        // Create container
        const container = document.createElement("div");
        // Calculate max size based on viewport
        const maxWidth = Math.min(window.innerWidth * 0.4, 1536); // 40% of window width, max 1536px
        const maxHeight = Math.min(window.innerHeight * 0.6, 1296); // 60% of window height, max 1296px

        container.className = "dialog-container";
        container.style.cssText = `
            position: fixed;
            top: 10%;
            left: 50%;
            transform: translateX(-50%);
            max-width: ${maxWidth}px;
            max-height: ${maxHeight}px;
        `;

        container.innerHTML = `
            <h3 class="dialog-title"> Endless 🌊✨ Node Spawner Drag Bar</h3>
            
            <div class="filter-section">
                <div class="filter-row">
                    <input type="text" class="filter-input" placeholder="Filter nodes..." title="Type to filter nodes, ↓ for history">
                    <div class="search-dropdown"></div>
                    <button class="expand-btn" title="Expand/collapse all categories">Expand All</button>
                </div>
                <div class="counters">
                    <span class="counter-selected">Selected: 0</span>
                    <span class="counter-total">Total: 0</span>
                </div>
            </div>

            <div class="recent-section"></div>
            <div class="node-list"></div>

            <div class="footer">
                <div class="btn-group">
                    <button id="spawn-btn" class="dialog-btn primary" title="Spawn selected nodes">🌊 Spawn Nodes</button>
                    <button id="clear-btn" class="dialog-btn" title="Clear all selections">Clear Selected</button>
                </div>
                <div class="btn-group">
                    <button id="clear-history-btn" class="dialog-btn" title="Clear search history">Clear Search</button>
                    <button id="clear-recent-btn" class="dialog-btn" title="Clear recent nodes">Clear Recent</button>
                    <button id="cancel-btn" class="dialog-btn secondary" title="Close dialog">❌ Cancel</button>
                </div>
            </div>
        `;

        document.body.appendChild(container);
        currentDialog = container;

        // Setup dragging
        makeDraggable(container, container.querySelector('.dialog-title'));

        // Build node data
        const nodes = Object.entries(LiteGraph.registered_node_types)
            .filter(([key, value]) => key && value)
            .map(([type, nodeClass]) => {
                const category = nodeClass.category || "Other";
                const displayName = nodeClass.title || nodeClass.name || type.split("/").pop();
                return {
                    type,
                    category,
                    pathParts: category.split("/"),
                    displayName,
                    description: nodeClass.desc || nodeClass.description || "",
                    fullPath: category + "/" + displayName
                };
            })
            .sort((a, b) => a.category.localeCompare(b.category) || a.displayName.localeCompare(b.displayName));

        allNodesData = nodes;
        
        // Render hierarchy
        const hierarchy = buildHierarchy(nodes);
        const tree = renderCategory(hierarchy);
        const nodeList = container.querySelector('.node-list');
        tree.forEach(section => nodeList.appendChild(section));

        // Add hover effects
        addButtonHoverEffects(container);

        // Setup event handlers
        const escHandler = setupEventHandlers();

        // Setup theme updates
        unregisterThemeCallback = onThemeChange(updateTheme);

        // Initialize
        updateRecentChips();
        updateSelectedCounter();
        updateTotalCounter();
        
        // Focus filter input
        container.querySelector('.filter-input').focus();

        // Setup cleanup
        const originalRemove = container.remove.bind(container);
        container.remove = function() {
            document.removeEventListener('keydown', escHandler);
            unregisterThemeCallback?.();
            document.getElementById('node-loader-style')?.remove();
            if (searchTimeout) clearTimeout(searchTimeout);
            if (hoverTimeout) clearTimeout(hoverTimeout);
            currentDialog = null;
            unregisterThemeCallback = null;
            searchTimeout = null;
            hoverTimeout = null;
            originalRemove();
        };
    }

    function closeDialog() {
        currentDialog?.remove();
    }

    // Register tool
    registerEndlessTool("Node Spawner", createNodeLoaderDialog);
})();