/*PyWry Plotly Widget*/

function render({ model, el }) {
    el.innerHTML = '';

    // Apply theme class to el (AnyWidget container) for proper theming
    // CSS rule on .pywry-theme-dark/.pywry-theme-light applies background-color
    const isDarkInitial = model.get('theme') === 'dark';
    el.classList.add(isDarkInitial ? 'pywry-theme-dark' : 'pywry-theme-light');

    const container = document.createElement('div');
    container.className = 'pywry-widget';
    container.classList.add(isDarkInitial ? 'pywry-theme-dark' : 'pywry-theme-light');
    const modelHeight = model.get('height');
    const modelWidth = model.get('width');
    if (modelHeight) {
        container.style.setProperty('--pywry-widget-height', modelHeight);
    }
    if (modelWidth) {
        container.style.setProperty('--pywry-widget-width', modelWidth);
    }
    container.style.overflow = 'visible';
    el.appendChild(container);

    // Set toast container for this widget instance
    if (window.PYWRY_TOAST && window.PYWRY_TOAST.setContainer) {
        window.PYWRY_TOAST.setContainer(container);
    }

    // Attach model to container for global dispatch lookup
    container._pywryModel = model;

    // Initialize global dispatcher if not present
    if (!window.pywry) {
        window.pywry = {};
    }

    // Local bridge - specialized for this widget instance
    const pywry = {
        _ready: false,
        _handlers: {},
        _pending: [],
        emit: function(type, data) {
            model.set('_js_event', JSON.stringify({ type, data, ts: Date.now() }));
            model.save_changes();
        },
        on: function(type, cb) {
            if (!this._handlers[type]) this._handlers[type] = [];
            this._handlers[type].push(cb);
            const pending = this._pending.filter(p => p.type === type);
            this._pending = this._pending.filter(p => p.type !== type);
            pending.forEach(p => cb(p.data));
        },
        _fire: function(type, data) {
            const handlers = this._handlers[type] || [];
            if (handlers.length === 0) {
                this._pending.push({ type, data });
            } else {
                handlers.forEach(h => h(data));
            }
        }
    };

    container._pywryInstance = pywry;

    // =========================================================================
    // TOOLBAR HANDLERS - LOADED FROM CENTRALIZED SOURCE
    // See: frontend/src/toolbar-handlers.js
    // =========================================================================
    __TOOLBAR_HANDLERS__
    // Helper function to trigger CSV download from data sent by Python
    function downloadCsv(csvContent, filename) {
        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = filename || 'data.csv';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }

    // Listen for CSV data from Python
    pywry.on('pywry:download-csv', (data) => {
        downloadCsv(data.csv, data.filename);
    });

    // Generic setComponentValue function for toolbar:set-value
    function setComponentValue(componentId, value, attrs) {
        const el = container.querySelector(`#${componentId}`);
        if (!el) return false;

        // SECURITY: Prevent setting secret values via state setter
        if (el.classList && el.classList.contains('pywry-input-secret')) {
            console.warn('[PyWry] Cannot set SecretInput value via toolbar:set-value. Use the event handler instead.');
            return false;
        }

        // Generic attribute setter - handles any attribute for any component
        if (attrs && typeof attrs === 'object') {
            Object.keys(attrs).forEach(attrName => {
                const attrValue = attrs[attrName];
                
                // Skip componentId, toolbarId, value (handled separately), options (handled separately)
                if (attrName === 'componentId' || attrName === 'toolbarId') return;
                
                switch (attrName) {
                    case 'label':
                    case 'text':
                        if (el.classList.contains('pywry-toolbar-button') || el.tagName === 'BUTTON') {
                            el.textContent = attrValue;
                        } else if (el.classList.contains('pywry-dropdown')) {
                            const textEl = el.querySelector('.pywry-dropdown-text');
                            if (textEl) textEl.textContent = attrValue;
                        } else if (el.classList.contains('pywry-checkbox') || el.classList.contains('pywry-toggle')) {
                            const labelEl = el.querySelector('.pywry-checkbox-label, .pywry-input-label');
                            if (labelEl) labelEl.textContent = attrValue;
                        } else {
                            const label = el.querySelector('.pywry-input-label');
                            if (label) label.textContent = attrValue;
                            else if (el.textContent !== undefined) el.textContent = attrValue;
                        }
                        break;
                        
                    case 'html':
                    case 'innerHTML':
                        if (el.classList.contains('pywry-toolbar-button') || el.tagName === 'BUTTON') {
                            el.innerHTML = attrValue;
                        } else if (el.classList.contains('pywry-dropdown')) {
                            const textEl = el.querySelector('.pywry-dropdown-text');
                            if (textEl) textEl.innerHTML = attrValue;
                        } else {
                            el.innerHTML = attrValue;
                        }
                        break;
                        
                    case 'disabled':
                        if (attrValue) {
                            el.setAttribute('disabled', 'disabled');
                            el.classList.add('pywry-disabled');
                            el.querySelectorAll('input, button, select, textarea').forEach(inp => {
                                inp.setAttribute('disabled', 'disabled');
                            });
                        } else {
                            el.removeAttribute('disabled');
                            el.classList.remove('pywry-disabled');
                            el.querySelectorAll('input, button, select, textarea').forEach(inp => {
                                inp.removeAttribute('disabled');
                            });
                        }
                        break;
                        
                    case 'variant':
                        if (el.classList.contains('pywry-toolbar-button') || el.tagName === 'BUTTON') {
                            const variants = ['primary', 'secondary', 'neutral', 'ghost', 'outline', 'danger', 'warning', 'icon'];
                            variants.forEach(v => el.classList.remove('pywry-btn-' + v));
                            if (attrValue && attrValue !== 'primary') {
                                el.classList.add('pywry-btn-' + attrValue);
                            }
                        }
                        break;
                        
                    case 'size':
                        if (el.classList.contains('pywry-toolbar-button') || el.tagName === 'BUTTON' || el.classList.contains('pywry-tab-group')) {
                            const sizes = ['xs', 'sm', 'lg', 'xl'];
                            sizes.forEach(s => {
                                el.classList.remove('pywry-btn-' + s);
                                el.classList.remove('pywry-tab-' + s);
                            });
                            if (attrValue) {
                                if (el.classList.contains('pywry-tab-group')) {
                                    el.classList.add('pywry-tab-' + attrValue);
                                } else {
                                    el.classList.add('pywry-btn-' + attrValue);
                                }
                            }
                        }
                        break;
                        
                    case 'description':
                    case 'tooltip':
                        if (attrValue) el.setAttribute('data-tooltip', attrValue);
                        else el.removeAttribute('data-tooltip');
                        break;
                        
                    case 'data':
                        if (attrValue) el.setAttribute('data-data', JSON.stringify(attrValue));
                        else el.removeAttribute('data-data');
                        break;
                        
                    case 'event':
                        el.setAttribute('data-event', attrValue);
                        break;
                        
                    case 'style':
                        if (typeof attrValue === 'string') {
                            el.style.cssText = attrValue;
                        } else if (typeof attrValue === 'object') {
                            Object.keys(attrValue).forEach(prop => {
                                el.style[prop] = attrValue[prop];
                            });
                        }
                        break;
                        
                    case 'className':
                    case 'class':
                        if (typeof attrValue === 'string') {
                            attrValue.split(' ').forEach(cls => { if (cls) el.classList.add(cls); });
                        } else if (typeof attrValue === 'object') {
                            if (attrValue.add) {
                                (Array.isArray(attrValue.add) ? attrValue.add : [attrValue.add]).forEach(cls => {
                                    if (cls) el.classList.add(cls);
                                });
                            }
                            if (attrValue.remove) {
                                (Array.isArray(attrValue.remove) ? attrValue.remove : [attrValue.remove]).forEach(cls => {
                                    if (cls) el.classList.remove(cls);
                                });
                            }
                        }
                        break;
                        
                    case 'checked':
                        const checkbox = el.querySelector('input[type="checkbox"]') || (el.type === 'checkbox' ? el : null);
                        if (checkbox) {
                            checkbox.checked = !!attrValue;
                            if (attrValue) el.classList.add('pywry-toggle-checked');
                            else el.classList.remove('pywry-toggle-checked');
                        }
                        break;
                        
                    case 'selected':
                        if (el.classList.contains('pywry-radio-group')) {
                            el.querySelectorAll('input[type="radio"]').forEach(radio => {
                                radio.checked = radio.value === attrValue;
                            });
                        } else if (el.classList.contains('pywry-tab-group')) {
                            el.querySelectorAll('.pywry-tab').forEach(tab => {
                                if (tab.dataset.value === attrValue) tab.classList.add('pywry-tab-active');
                                else tab.classList.remove('pywry-tab-active');
                            });
                        }
                        break;
                        
                    case 'placeholder':
                        const input = el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' ? el : el.querySelector('input, textarea');
                        if (input) input.setAttribute('placeholder', attrValue);
                        break;
                        
                    case 'min':
                    case 'max':
                    case 'step':
                        const numInput = el.tagName === 'INPUT' ? el : el.querySelector('input[type="number"], input[type="range"]');
                        if (numInput) numInput.setAttribute(attrName, attrValue);
                        break;
                        
                    case 'options':
                    case 'value':
                        // Handled separately below
                        break;
                        
                    default:
                        if (attrName.startsWith('data-')) {
                            el.setAttribute(attrName, attrValue);
                        } else {
                            try {
                                if (attrName in el) el[attrName] = attrValue;
                                else el.setAttribute(attrName, attrValue);
                            } catch (e) {
                                el.setAttribute(attrName, attrValue);
                            }
                        }
                }
            });
        }

        // Handle value and options (backward compatible behavior)
        const options = attrs && attrs.options;
        if (value === undefined && attrs && attrs.value !== undefined) {
            value = attrs.value;
        }

        if (el.tagName === 'SELECT' || el.tagName === 'INPUT') {
            if (value !== undefined) el.value = value;
            return true;
        } else if (el.classList.contains('pywry-dropdown')) {
            if (options && Array.isArray(options)) {
                const menu = el.querySelector('.pywry-dropdown-menu');
                if (menu) {
                    menu.innerHTML = options.map(opt => {
                        const isSelected = String(opt.value) === String(value);
                        return '<div class="pywry-dropdown-option' + (isSelected ? ' pywry-selected' : '') +
                               '" data-value="' + opt.value + '">' + opt.label + '</div>';
                    }).join('');
                }
            }
            if (value !== undefined) {
                const textEl = el.querySelector('.pywry-dropdown-text');
                if (textEl) {
                    const optionEl = el.querySelector('.pywry-dropdown-option[data-value="' + value + '"]');
                    if (optionEl) {
                        textEl.textContent = optionEl.textContent;
                        el.querySelectorAll('.pywry-dropdown-option').forEach(opt => {
                            opt.classList.remove('pywry-selected');
                        });
                        optionEl.classList.add('pywry-selected');
                    }
                }
            }
            return true;
        } else if (el.classList.contains('pywry-multiselect')) {
            if (value !== undefined) {
                const values = Array.isArray(value) ? value : [value];
                el.querySelectorAll('input[type="checkbox"]').forEach(cb => {
                    cb.checked = values.includes(cb.value);
                });
            }
            return true;
        } else if (el.classList.contains('pywry-toggle')) {
            if (value !== undefined) {
                const checkbox = el.querySelector('input[type="checkbox"]');
                if (checkbox) {
                    checkbox.checked = !!value;
                    if (value) el.classList.add('pywry-toggle-checked');
                    else el.classList.remove('pywry-toggle-checked');
                }
            }
            return true;
        } else if (el.classList.contains('pywry-checkbox')) {
            if (value !== undefined) {
                const checkbox = el.querySelector('input[type="checkbox"]');
                if (checkbox) checkbox.checked = !!value;
            }
            return true;
        } else if (el.classList.contains('pywry-radio-group')) {
            if (value !== undefined) {
                el.querySelectorAll('input[type="radio"]').forEach(radio => {
                    radio.checked = radio.value === value;
                });
            }
            return true;
        } else if (el.classList.contains('pywry-tab-group')) {
            if (value !== undefined) {
                el.querySelectorAll('.pywry-tab').forEach(tab => {
                    if (tab.dataset.value === value) tab.classList.add('pywry-tab-active');
                    else tab.classList.remove('pywry-tab-active');
                });
            }
            return true;
        } else if (el.classList.contains('pywry-range-group')) {
            if (attrs && (attrs.start !== undefined || attrs.end !== undefined)) {
                const startInput = el.querySelector('input[data-range="start"]');
                const endInput = el.querySelector('input[data-range="end"]');
                const fill = el.querySelector('.pywry-range-track-fill');
                const startDisp = el.querySelector('.pywry-range-start-value');
                const endDisp = el.querySelector('.pywry-range-end-value');
                
                if (startInput && attrs.start !== undefined) startInput.value = attrs.start;
                if (endInput && attrs.end !== undefined) endInput.value = attrs.end;
                
                if (fill && startInput && endInput) {
                    const min = parseFloat(startInput.min) || 0;
                    const max = parseFloat(startInput.max) || 100;
                    const range = max - min;
                    const startVal = parseFloat(startInput.value);
                    const endVal = parseFloat(endInput.value);
                    const startPct = ((startVal - min) / range) * 100;
                    const endPct = ((endVal - min) / range) * 100;
                    fill.style.left = startPct + '%';
                    fill.style.width = (endPct - startPct) + '%';
                }
                if (startDisp && attrs.start !== undefined) startDisp.textContent = attrs.start;
                if (endDisp && attrs.end !== undefined) endDisp.textContent = attrs.end;
            }
            return true;
        } else if (el.classList.contains('pywry-input-range') || (el.tagName === 'INPUT' && el.type === 'range')) {
            if (value !== undefined) {
                el.value = value;
                const display = el.nextElementSibling;
                if (display && display.classList.contains('pywry-range-value')) {
                    display.textContent = value;
                }
            }
            return true;
        }
        
        if (value !== undefined && 'value' in el) {
            el.value = value;
            return true;
        }
        
        return attrs && Object.keys(attrs).length > 0;
    }

    // Listen for toolbar:set-value to update any component's attributes
    pywry.on('toolbar:set-value', (data) => {
        if (data && data.componentId) {
            setComponentValue(data.componentId, data.value, data);
        }
    });

    // Helper function to process config - converts icon names to objects and event props to click handlers
    function processPlotlyConfig(config) {
        if (!config) return config;

        if (config.modeBarButtonsToAdd && Array.isArray(config.modeBarButtonsToAdd)) {
            config.modeBarButtonsToAdd = config.modeBarButtonsToAdd.map(function(btn, idx) {

                // Handle icon - could be string (Plotly icon name) or object (custom SVG)
                if (typeof btn.icon === 'string') {
                    // String icon name - look up in Plotly.Icons
                    var iconName = btn.icon;
                    if (window.Plotly && window.Plotly.Icons && window.Plotly.Icons[iconName]) {
                        btn.icon = window.Plotly.Icons[iconName];
                    } else {
                        // Fallback: use question mark icon if the named icon doesn't exist
                        console.warn('[PyWry Plotly] Unknown icon:', iconName, '- using fallback');
                        if (window.Plotly && window.Plotly.Icons && window.Plotly.Icons.question) {
                            btn.icon = window.Plotly.Icons.question;
                        } else {
                            btn.icon = {
                                width: 857.1,
                                height: 1000,
                                path: 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z',
                                transform: 'matrix(1 0 0 -1 0 850)'
                            };
                        }
                    }
                } else if (btn.icon && typeof btn.icon === 'object') {
                    // Object icon - custom SVG definition from Python SvgIcon
                    // Ensure it has required properties for Plotly
                    if (!btn.icon.width) btn.icon.width = 1000;
                    if (!btn.icon.height) btn.icon.height = 1000;
                }

                // Convert 'event' property to 'click' function
                if (btn.event) {
                    var eventName = btn.event;
                    var eventData = btn.data || {};
                    btn.click = function(gd) {
                        pywry.emit(eventName, eventData);
                    };
                    delete btn.event;
                    delete btn.data;
                }

                return btn;
            });
        }

        return config;
    }

    pywry.on('plotly:update-figure', (data) => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly) {
            // Python sends {data: [...], layout: {...}} directly, not nested in figure
            const figData = data.figure ? data.figure.data : data.data;
            const figLayout = data.figure ? data.figure.layout : data.layout;
            // Process config to convert icon names and event properties, always hide logo
            const config = Object.assign({displaylogo: false}, processPlotlyConfig(data.config || {}));
            if (figData) {
                window.Plotly.react(plotDiv, figData, figLayout || {}, config);
            }
        }
    });

    pywry.on('plotly:update-layout', (data) => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly && data.layout) {
            window.Plotly.relayout(plotDiv, data.layout);
        }
    });

    pywry.on('plotly:update-traces', (data) => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly && data.update) {
            window.Plotly.restyle(plotDiv, data.update, data.indices);
        }
    });

    pywry.on('plotly:reset-zoom', () => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly) {
            window.Plotly.relayout(plotDiv, {
                'xaxis.autorange': true,
                'yaxis.autorange': true
            });
        }
    });

    pywry.on('plotly:request-state', () => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly) {
            pywry.emit('plotly:state-response', {
                layout: plotDiv.layout,
                data: plotDiv.data,
                chartId: model.get('chart_id')
            });
        }
    });

    // Also allow export to be triggered from Python
    pywry.on('plotly:export-data', () => {
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv) doExportData(plotDiv);
    });

    pywry.on('pywry:update-theme', (data) => {
        if (data && data.theme) {
            const isDark = data.theme.includes('dark');
            const newTheme = isDark ? 'dark' : 'light';
            model.set('theme', newTheme);
            model.save_changes();
            applyTheme();
        }
    });

    // Handle alert/toast notifications
    pywry.on('pywry:alert', (data) => {
        if (window.PYWRY_TOAST) {
            const type = data.type || 'info';
            if (type === 'confirm') {
                window.PYWRY_TOAST.confirm({
                    message: data.message || data.text || '',
                    title: data.title,
                    position: data.position,
                    container: container,
                    onConfirm: () => {
                        if (data.callback_event) {
                            pywry.emit(data.callback_event, { confirmed: true });
                        }
                    },
                    onCancel: () => {
                        if (data.callback_event) {
                            pywry.emit(data.callback_event, { confirmed: false });
                        }
                    }
                });
            } else {
                window.PYWRY_TOAST.show({ ...data, container: container });
            }
        } else {
            // Fallback to browser alert
            const message = data.message || data.text || '';
            alert(message);
        }
    });

    model.off('change:_py_event');
    model.off('change:content');
    model.off('change:figure_json');
    model.off('change:theme');

    model.on('change:_py_event', () => {
        try {
            const event = JSON.parse(model.get('_py_event') || '{}');
            if (event.type) {
                // Fire locally
                pywry._fire(event.type, event.data);

                // Bubbling: fire to global pywry only if not handled locally
                const inlineHandledEvents = ['pywry:update-theme', 'pywry:alert'];
                if (!inlineHandledEvents.includes(event.type) && window.pywry && window.pywry._fire && window.pywry !== pywry) {
                    window.pywry._fire(event.type, event.data);
                }
            }
        } catch(e) {}
    });

    function applyTheme() {
        const isDark = model.get('theme') === 'dark';
        // Update el (AnyWidget container) theme
        el.classList.remove('pywry-theme-dark', 'pywry-theme-light');
        el.classList.add(isDark ? 'pywry-theme-dark' : 'pywry-theme-light');
        // Update container theme
        container.classList.remove('pywry-theme-dark', 'pywry-theme-light');
        container.classList.add(isDark ? 'pywry-theme-dark' : 'pywry-theme-light');

        // relayout avoids carrying stale colours from the old layout.
        const plotDiv = container.querySelector('.js-plotly-plot');
        if (plotDiv && window.Plotly && plotDiv.data) {
            const templateName = isDark ? 'plotly_dark' : 'plotly_white';
            if (window.__pywryMergeThemeTemplate) {
                const mergedTemplate = window.__pywryMergeThemeTemplate(plotDiv, templateName);
                if (window.__pywryStripThemeColors) window.__pywryStripThemeColors(plotDiv);
                window.Plotly.relayout(plotDiv, { template: mergedTemplate });
            } else {
                const template = window.PYWRY_PLOTLY_TEMPLATES?.[templateName];
                if (template) {
                    if (window.__pywryStripThemeColors) window.__pywryStripThemeColors(plotDiv);
                    window.Plotly.relayout(plotDiv, { template: template });
                }
            }
        }
    }

    function setupPlotlyEvents(chartEl) {
        // Get chartId from model for event payloads
        const chartId = model.get('chart_id') || 'default';

        chartEl.on('plotly_click', function(data) {
            const points = data.points.map(p => ({
                curveNumber: p.curveNumber,
                pointNumber: p.pointNumber,
                pointIndex: p.pointIndex,
                x: p.x,
                y: p.y,
                z: p.z,
                text: p.text,
                customdata: p.customdata,
                data: p.data,
                trace_name: p.data ? p.data.name : null
            }));
            pywry.emit('plotly:click', {
                chartId: chartId,
                widget_type: 'chart',
                points: points,
                point_indices: points.map(p => p.pointNumber),
                curve_number: points.length > 0 ? points[0].curveNumber : null,
                event: data.event
            });
        });
        chartEl.on('plotly_hover', function(data) {
            const points = data.points.map(p => ({
                curveNumber: p.curveNumber,
                pointNumber: p.pointNumber,
                pointIndex: p.pointIndex,
                x: p.x,
                y: p.y,
                z: p.z,
                text: p.text,
                customdata: p.customdata,
                data: p.data,
                trace_name: p.data ? p.data.name : null
            }));
            pywry.emit('plotly:hover', {
                chartId: chartId,
                widget_type: 'chart',
                points: points,
                point_indices: points.map(p => p.pointNumber),
                curve_number: points.length > 0 ? points[0].curveNumber : null
            });
        });
        chartEl.on('plotly_selected', function(data) {
            if (data) {
                const points = data.points.map(p => ({
                    curveNumber: p.curveNumber,
                    pointNumber: p.pointNumber,
                    pointIndex: p.pointIndex,
                    x: p.x,
                    y: p.y,
                    z: p.z,
                    text: p.text,
                    customdata: p.customdata,
                    data: p.data,
                    trace_name: p.data ? p.data.name : null
                }));
                pywry.emit('plotly:selected', {
                    chartId: chartId,
                    widget_type: 'chart',
                    points: points,
                    point_indices: points.map(p => p.pointNumber),
                    range: data.range || null,
                    lassoPoints: data.lassoPoints || null
                });
            } else {
                pywry.emit('plotly:selected', {
                    chartId: chartId,
                    widget_type: 'chart',
                    points: [],
                    point_indices: [],
                    range: null,
                    lassoPoints: null
                });
            }
        });
        chartEl.on('plotly_relayout', function(data) {
            pywry.emit('plotly:relayout', { chartId: chartId, widget_type: 'chart', relayout_data: data });
        });
    }

    function renderContent() {
        const content = model.get('content');
        const figureJson = model.get('figure_json');

        if (!content) {
            container.innerHTML = '<div style="padding:20px;color:#888;font-family:monospace;">Waiting for content...</div>';
            return;
        }

        // Set content HTML (toolbar + chart container)
        container.innerHTML = content;

        // Initialize toolbar handlers using centralized function
        // This handles all component types: Button, Select, MultiSelect, Toggle, Checkbox, etc.
        setTimeout(() => initToolbarHandlers(container, pywry), 10);

        applyTheme();

        // Find chart element within our container (NOT document.getElementById!)
        const chartEl = container.querySelector('#chart');

        if (!chartEl) {
            console.error('[PyWry Plotly] No #chart element found in content');
            return;
        }

        if (!window.Plotly) {
            console.error('[PyWry Plotly] Plotly library not available');
            chartEl.innerHTML = '<div style="background:#ff4444;color:white;padding:20px;">Plotly not loaded</div>';
            return;
        }

        // Use figure_json from model if available
        if (figureJson) {
            try {
                const figData = JSON.parse(figureJson);
                const config = figData.config || {};

                // Extract per-theme user template overrides (PyWry extension)
                const userTemplateDark = config.templateDark || null;
                const userTemplateLight = config.templateLight || null;
                delete config.templateDark;
                delete config.templateLight;

                // Extract single/legacy template from layout
                let userTemplate = null;
                const templates = window.PYWRY_PLOTLY_TEMPLATES || {};
                const isDark = model.get('theme') === 'dark';
                const themeTemplateName = isDark ? 'plotly_dark' : 'plotly_white';
                if (figData.layout && typeof figData.layout.template === 'string' && templates[figData.layout.template]) {
                    if (figData.layout.template !== themeTemplateName) {
                        userTemplate = templates[figData.layout.template];
                    }
                    figData.layout.template = null;
                } else if (figData.layout && figData.layout.template && typeof figData.layout.template === 'object') {
                    userTemplate = figData.layout.template;
                    figData.layout.template = null;
                }

                // Process modebar buttons using shared helper
                processPlotlyConfig(config);

                const finalConfig = Object.assign({responsive: true, displaylogo: false}, config);

                window.Plotly.newPlot(chartEl, figData.data, figData.layout, finalConfig).then(function() {
                    // Apply merged theme template (user always wins)
                    if (window.__pywryMergeThemeTemplate) {
                        const merged = window.__pywryMergeThemeTemplate(chartEl, themeTemplateName, userTemplate, userTemplateDark, userTemplateLight);
                        window.Plotly.relayout(chartEl, { template: merged });
                    }
                    chartEl.__pywry_theme_template__ = themeTemplateName;
                    setupPlotlyEvents(chartEl);
                }).catch(function(err) {
                    console.error('[PyWry Plotly] Plotly.newPlot failed:', err);
                    chartEl.innerHTML = '<div style="background:#ff4444;color:white;padding:20px;">Plotly error: ' + err.message + '</div>';
                });
            } catch(e) {
                console.error('[PyWry Plotly] Failed to parse figure_json:', e);
            }
        } else {
            console.warn('[PyWry Plotly] No figure_json provided, chart will be empty');
        }

        pywry._ready = true;
    }

    model.on('change:content', renderContent);
    model.on('change:figure_json', renderContent);
    model.on('change:theme', applyTheme);
    renderContent();
}

export default { render };
