#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright Daniel Harding - RomanAILabs
4DLLM Runner GUI — World-Class PyQt6 + HTML/CSS/JS Interface

A stunning graphical interface built with PyQt6 and modern web technologies
for running 4DLLM files with maximum visual quality.
"""

from __future__ import annotations

import json
import os
import queue
import select
import struct
import subprocess
import sys
import threading
import time
import zlib
import ast
import shlex
import logging
import importlib.util
import html
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from datetime import datetime

# For non-blocking I/O
try:
    import fcntl
    HAS_FCNTL = True
except ImportError:
    HAS_FCNTL = False

try:
    from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                                 QHBoxLayout, QPushButton, QLabel, QTextEdit, 
                                 QFileDialog, QMessageBox, QComboBox, QCheckBox,
                                 QLineEdit, QSplitter, QTreeWidget, QTreeWidgetItem,
                                 QTabWidget, QFrame, QScrollArea, QInputDialog, QDialog)
    from PyQt6.QtCore import Qt, QUrl, QThread, pyqtSignal, pyqtSlot, QTimer, QSize, QEventLoop
    from PyQt6.QtGui import QFont, QColor, QPalette, QIcon, QClipboard
    from PyQt6.QtWebEngineWidgets import QWebEngineView
    from PyQt6.QtWebChannel import QWebChannel
    HAS_PYQT6 = True
except ImportError:
    try:
        from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                                      QHBoxLayout, QPushButton, QLabel, QTextEdit,
                                      QFileDialog, QMessageBox, QComboBox, QCheckBox,
                                      QLineEdit, QSplitter, QTreeWidget, QTreeWidgetItem,
                                      QTabWidget, QFrame, QScrollArea, QInputDialog, QDialog)
        from PySide6.QtCore import Qt, QUrl, QThread, Signal, Slot, QTimer, QSize, QEventLoop
        from PySide6.QtGui import QFont, QColor, QPalette, QIcon, QClipboard
        from PySide6.QtWebEngineWidgets import QWebEngineView
        from PySide6.QtWebChannel import QWebChannel
        HAS_PYQT6 = True
        pyqtSignal = Signal
        pyqtSlot = Slot
    except ImportError:
        HAS_PYQT6 = False
        # If Qt is not available, we can't run the GUI
        print("ERROR: PyQt6 or PySide6 is required but not installed.")
        print("Please install with: pip install PyQt6")
        print("Or: pip install PySide6")
        sys.exit(1)

# Import shared utilities from original file using importlib
import importlib.util

# Setup logger (must be before functions that use it)
DEBUG_LOG_FILE = Path(__file__).parent / "4dllm_runner_debug.log"
DEBUG_LOGGER = logging.getLogger("4dllm_runner_gui_qt")
DEBUG_LOGGER.setLevel(logging.DEBUG)
fh = logging.FileHandler(DEBUG_LOG_FILE, mode='a', encoding='utf-8')
fh.setLevel(logging.DEBUG)
# Console handler for verbose output
ch = logging.StreamHandler(sys.stderr)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
DEBUG_LOGGER.addHandler(fh)
DEBUG_LOGGER.addHandler(ch)

# Default system prompt fallback (SimpleForge style)
DEFAULT_SYSTEM_PROMPT = (
    "You are RomanAI, a spacetime-aware coding and reasoning assistant created by "
    "RomanAILabs. Be clear, honest, and helpful."
)

def _import_runner_utils():
    """Import utilities from 4dllm_runner.py"""
    runner_path = Path(__file__).parent / "4dllm_runner.py"
    if runner_path.exists():
        try:
            DEBUG_LOGGER.info(f"Loading runner module from: {runner_path}")
            spec = importlib.util.spec_from_file_location("runner_module", str(runner_path))
            if not spec:
                DEBUG_LOGGER.error(f"Failed to create spec for runner module from: {runner_path}")
                return None
            if not spec.loader:
                DEBUG_LOGGER.error(f"Spec loader is None for runner module. Spec: {spec}")
                # Try alternative import method
                try:
                    import sys
                    # Add parent directory to path if needed
                    parent_dir = str(runner_path.parent)
                    if parent_dir not in sys.path:
                        sys.path.insert(0, parent_dir)
                    # Try importing directly
                    runner_module = importlib.import_module("4dllm_runner")
                    DEBUG_LOGGER.info("Runner module loaded via direct import")
                    return runner_module
                except Exception as alt_error:
                    DEBUG_LOGGER.error(f"Alternative import also failed: {alt_error}", exc_info=True)
                    return None
            
            runner_module = importlib.util.module_from_spec(spec)
            if runner_module is None:
                DEBUG_LOGGER.error("module_from_spec returned None")
                return None
            
            # Python 3.12 dataclass fix: Register module in sys.modules before exec_module
            # This prevents AttributeError: 'NoneType' object has no attribute '__dict__'
            import sys
            sys.modules['runner_module'] = runner_module
            
            spec.loader.exec_module(runner_module)
            DEBUG_LOGGER.info("Runner module loaded successfully")
            return runner_module
        except Exception as e:
            DEBUG_LOGGER.error(f"Failed to load runner module: {e}", exc_info=True)
            import traceback
            DEBUG_LOGGER.error(f"Traceback: {traceback.format_exc()}")
    else:
        DEBUG_LOGGER.error(f"Runner module file not found: {runner_path}")
    return None

def _import_gui_utils():
    """Import utilities from 4dllm_runner_gui.py"""
    gui_path = Path(__file__).parent / "4dllm_runner_gui.py"
    if gui_path.exists():
        try:
            spec = importlib.util.spec_from_file_location("gui_module", str(gui_path))
            if spec and spec.loader:
                gui_module = importlib.util.module_from_spec(spec)
                # Python 3.12 dataclass fix: Register module in sys.modules before exec_module
                import sys
                sys.modules['gui_module'] = gui_module
                spec.loader.exec_module(gui_module)
                return gui_module
        except Exception as e:
            DEBUG_LOGGER.error(f"Failed to load GUI module: {e}")
    return None

# ========== EDITOR-SPECIFIC DEBUG LOGGER ==========
EDITOR_DEBUG_LOG_FILE = Path(__file__).parent / "editor_debugger.log"
# Force create log file immediately
try:
    with open(EDITOR_DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
        f.write(f"\n{'='*80}\n")
        f.write(f"EDITOR DEBUG LOGGER - SESSION START: {datetime.now().isoformat()}\n")
        f.write(f"{'='*80}\n")
except Exception as e:
    print(f"Warning: Could not create editor_debugger.log: {e}", file=sys.stderr)

EDITOR_LOGGER = logging.getLogger("4dllm_editor")
EDITOR_LOGGER.setLevel(logging.DEBUG)
# Remove existing handlers to avoid duplicates
EDITOR_LOGGER.handlers = []
editor_fh = logging.FileHandler(EDITOR_DEBUG_LOG_FILE, mode='a', encoding='utf-8')
editor_fh.setLevel(logging.DEBUG)
editor_formatter = logging.Formatter(
    '%(asctime)s [%(levelname)s] [Thread:%(threadName)s:%(thread)d] %(funcName)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
editor_fh.setFormatter(editor_formatter)
# Force immediate flush after each log entry
editor_fh.terminator = '\n'
EDITOR_LOGGER.addHandler(editor_fh)
EDITOR_LOGGER.propagate = False  # Prevent duplicate logs in parent logger

# Write initialization message and force flush
EDITOR_LOGGER.info("=" * 80)
EDITOR_LOGGER.info("EDITOR DEBUG LOGGER INITIALIZED")
EDITOR_LOGGER.info("=" * 80)
editor_fh.flush()

# ========== VERBOSE BUILDER JSON DEBUG LOGGER ==========
DEBUG_BUILDER_JSON = Path(__file__).parent / "debug_builder.json"

class BuilderDebugLogger:
    """Ultra-verbose JSON debug logger for builder operations"""
    
    def __init__(self, json_file: Path):
        self.json_file = json_file
        self.log_data = {
            "session_start": datetime.now().isoformat(),
            "events": [],
            "python_storage_snapshots": [],
            "javascript_snapshots": [],
            "build_attempts": [],
            "script_loads": [],
            "errors": []
        }
        self._lock = threading.Lock()
        self._write_immediate()
    
    def _write_immediate(self):
        """Write log data to JSON file immediately"""
        try:
            with self._lock:
                with open(self.json_file, 'w', encoding='utf-8') as f:
                    json.dump(self.log_data, f, indent=2, ensure_ascii=False)
        except Exception as e:
            DEBUG_LOGGER.error(f"Failed to write debug_builder.json: {e}")
    
    def log_event(self, event_type: str, details: Dict[str, Any], timestamp: Optional[str] = None):
        """Log a builder event"""
        event = {
            "timestamp": timestamp or datetime.now().isoformat(),
            "type": event_type,
            "details": details
        }
        with self._lock:
            self.log_data["events"].append(event)
            # Keep last 1000 events
            if len(self.log_data["events"]) > 1000:
                self.log_data["events"] = self.log_data["events"][-1000:]
        self._write_immediate()
        DEBUG_LOGGER.debug(f"[BUILDER_DEBUG] {event_type}: {json.dumps(details, default=str)}")
    
    def snapshot_python_storage(self, scripts: List[Dict[str, Any]], context: str):
        """Snapshot Python storage state"""
        snapshot = {
            "timestamp": datetime.now().isoformat(),
            "context": context,
            "count": len(scripts),
            "scripts": [
                {
                    "name": s.get("name", "N/A"),
                    "enabled": s.get("enabled", False),
                    "content_length": len(s.get("content", "")),
                    "priority": s.get("priority", 0),
                    "imports_count": len(s.get("imports", [])),
                    "has_content": bool(s.get("content")),
                    "source_path": s.get("source_path", "N/A")
                }
                for s in scripts
            ]
        }
        with self._lock:
            self.log_data["python_storage_snapshots"].append(snapshot)
            if len(self.log_data["python_storage_snapshots"]) > 100:
                self.log_data["python_storage_snapshots"] = self.log_data["python_storage_snapshots"][-100:]
        self._write_immediate()
    
    def snapshot_javascript_state(self, state_info: Dict[str, Any], context: str):
        """Snapshot JavaScript state"""
        snapshot = {
            "timestamp": datetime.now().isoformat(),
            "context": context,
            "state": state_info
        }
        with self._lock:
            self.log_data["javascript_snapshots"].append(snapshot)
            if len(self.log_data["javascript_snapshots"]) > 100:
                self.log_data["javascript_snapshots"] = self.log_data["javascript_snapshots"][-100:]
        self._write_immediate()
    
    def log_build_attempt(self, attempt_id: str, details: Dict[str, Any]):
        """Log a build attempt"""
        attempt = {
            "id": attempt_id,
            "timestamp": datetime.now().isoformat(),
            **details
        }
        with self._lock:
            self.log_data["build_attempts"].append(attempt)
            if len(self.log_data["build_attempts"]) > 50:
                self.log_data["build_attempts"] = self.log_data["build_attempts"][-50:]
        self._write_immediate()
    
    def log_script_load(self, load_id: str, details: Dict[str, Any]):
        """Log a script loading operation"""
        load = {
            "id": load_id,
            "timestamp": datetime.now().isoformat(),
            **details
        }
        with self._lock:
            self.log_data["script_loads"].append(load)
            if len(self.log_data["script_loads"]) > 100:
                self.log_data["script_loads"] = self.log_data["script_loads"][-100:]
        self._write_immediate()
    
    def log_error(self, error_type: str, error_details: Dict[str, Any], traceback_str: Optional[str] = None):
        """Log an error"""
        error = {
            "timestamp": datetime.now().isoformat(),
            "type": error_type,
            "details": error_details,
            "traceback": traceback_str
        }
        with self._lock:
            self.log_data["errors"].append(error)
            if len(self.log_data["errors"]) > 200:
                self.log_data["errors"] = self.log_data["errors"][-200:]
        self._write_immediate()

# Global builder debug logger instance
BUILDER_DEBUG = BuilderDebugLogger(DEBUG_BUILDER_JSON)
DEBUG_LOGGER.info(f"Builder debug logger initialized: {DEBUG_BUILDER_JSON}")

# GUI Debug Log Handler (will be set up after GUI is created)
class GUIDebugLogHandler(logging.Handler):
    """Custom handler that forwards log messages to the GUI debug log"""
    def __init__(self, gui_instance):
        super().__init__()
        self.gui_instance = gui_instance
        self.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(levelname)s: %(message)s')
        self.setFormatter(formatter)
    
    def emit(self, record):
        """Emit a log record to the GUI"""
        try:
            if self.gui_instance and hasattr(self.gui_instance, '_append_debug_log'):
                msg = self.format(record)
                level_map = {
                    'DEBUG': 'debug',
                    'INFO': 'info',
                    'WARNING': 'warning',
                    'ERROR': 'error',
                    'CRITICAL': 'error'
                }
                level = level_map.get(record.levelname, 'info')
                self.gui_instance._append_debug_log(msg, level)
        except Exception:
            pass  # Don't let logging errors break the app

# Import utilities
runner_module = _import_runner_utils()
gui_module = _import_gui_utils()

# ============================================================================
# EMBEDDED 4DLLM BUILDER V2.0 - Directly integrated to avoid import issues
# ============================================================================

# ---------------- 4DLLM Builder Constants ----------------
FOURDLLM_MAGIC = b"4DLL"
FOURDLLM_VERSION = 2
FOURDLLM_HEADER_SIZE = 64

TOC_MAGIC = b"TOC1"
TOC_VERSION = 1

# Section Types
SECTION_GGUF_DATA = 0x01
SECTION_PYTHON_SCRIPT = 0x02
SECTION_METADATA = 0x03
SECTION_SCRIPT_CONFIG = 0x04
SECTION_MANIFEST = 0x05

# Flags
FLAG_COMPRESSED_ZLIB = 0x01

# Structs
SECTION_HDR_STRUCT = struct.Struct("<BB2xQQII")   # type, flags, size_c, size_u, extra_sz, crc32
TOC_HDR_STRUCT = struct.Struct("<4sII")          # magic, toc_version, section_count
TOC_ENTRY_STRUCT = struct.Struct("<BB2xQQQII")   # type, flags, offset, size_c, size_u, extra_sz, crc32

def utc_iso() -> str:
    return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

def _sha256_bytes(b: bytes) -> str:
    import hashlib
    return hashlib.sha256(b).hexdigest()

# ---------------- Module Validation & Auto-Correction ----------------

def validate_and_fix_script(content: str, script_name: str) -> Tuple[str, List[str]]:
    """
    INTELLIGENT script validation and auto-correction for 4DLLM compatibility.
    Fixes common issues that prevent scripts from working properly in the 4DLLM environment.
    """
    warnings = []
    fixed = content
    
    # Ensure we have content
    if not fixed or not fixed.strip():
        warnings.append("Warning: Script is empty or contains only whitespace")
        return fixed, warnings
    
    lines = fixed.split('\n')
    original_line_count = len(lines)
    
    # ========== PHASE 1: Fix encoding and line endings ==========
    # Normalize line endings (remove \r, ensure \n)
    fixed = fixed.replace('\r\n', '\n').replace('\r', '\n')
    lines = fixed.split('\n')
    
    # ========== PHASE 2: Fix __future__ imports positioning ==========
    future_imports = []
    future_import_indices = []
    seen_other_code = False
    in_docstring = False
    docstring_quote = None
    
    for i, line in enumerate(lines):
        stripped = line.strip()
        
        # Skip shebang
        if i == 0 and stripped.startswith('#!'):
            continue
        
        # Track docstrings
        if not in_docstring:
            if stripped.startswith('"""') or stripped.startswith("'''"):
                docstring_quote = '"""' if stripped.startswith('"""') else "'''"
                in_docstring = True
                if stripped.count(docstring_quote) >= 2:
                    in_docstring = False
                continue
        else:
            if docstring_quote in stripped:
                in_docstring = False
            continue
        
        # Track __future__ imports
        if stripped.startswith('from __future__'):
            if seen_other_code:
                warnings.append(f"Line {i+1}: 'from __future__' import found after other code. Moving to top.")
                future_imports.append(line)
                future_import_indices.append(i)
        elif stripped and not stripped.startswith('#') and not in_docstring:
            seen_other_code = True
    
    # Move __future__ imports to correct position
    if future_import_indices:
        for idx in reversed(future_import_indices):
            lines.pop(idx)
        
        # Find insertion point (after shebang/docstring, before other imports)
        insert_pos = 0
        in_docstring = False
        docstring_quote = None
        
        for i, line in enumerate(lines):
            stripped = line.strip()
            if i == 0 and stripped.startswith('#!'):
                insert_pos = i + 1
                continue
            if not in_docstring:
                if stripped.startswith('"""') or stripped.startswith("'''"):
                    docstring_quote = '"""' if stripped.startswith('"""') else "'''"
                    in_docstring = True
                    if stripped.count(docstring_quote) >= 2:
                        in_docstring = False
                        insert_pos = i + 1
                    else:
                        for j in range(i + 1, len(lines)):
                            if docstring_quote in lines[j]:
                                insert_pos = j + 1
                                in_docstring = False
                                break
                    continue
            else:
                if docstring_quote and docstring_quote in stripped:
                    in_docstring = False
                    insert_pos = i + 1
                continue
            if stripped and not stripped.startswith('#'):
                break
            insert_pos = i + 1
        
        # Insert future imports
        for future_import in reversed(future_imports):
            lines.insert(insert_pos, future_import)
        
        fixed = '\n'.join(lines)
        warnings.append(f"✓ Fixed: Moved {len(future_imports)} 'from __future__' import(s) to correct position.")
    
    # ========== PHASE 3: Fix common syntax issues ==========
    # Remove trailing whitespace that can cause issues
    fixed_lines = [line.rstrip() for line in lines]
    fixed = '\n'.join(fixed_lines)
    
    # Fix mixed indentation (tabs to spaces)
    if '\t' in fixed:
        fixed = fixed.replace('\t', '    ')  # Convert tabs to 4 spaces
        warnings.append("✓ Fixed: Converted tabs to spaces for consistent indentation.")
    
    # ========== PHASE 4: Validate syntax with intelligent error reporting ==========
    syntax_errors = []
    try:
        compile(fixed, script_name, "exec")
    except SyntaxError as e:
        error_msg = f"Syntax error at line {e.lineno}: {e.msg}"
        syntax_errors.append(error_msg)
        warnings.append(f"⚠ {error_msg}")
        
        # Try to fix common syntax issues
        # Fix unclosed strings (basic attempt)
        if "EOL while scanning string literal" in str(e.msg) or "unterminated string literal" in str(e.msg):
            # This is complex - just warn
            warnings.append("⚠ Unclosed string detected - manual review recommended")
        
        # Fix missing colons after if/for/while/def/class
        if "expected ':'" in str(e.msg):
            line_num = e.lineno - 1
            if 0 <= line_num < len(fixed_lines):
                line = fixed_lines[line_num]
                if line.strip() and not line.strip().endswith(':'):
                    # Check if it should have a colon
                    stripped = line.strip()
                    for keyword in ['if', 'elif', 'else', 'for', 'while', 'def', 'class', 'try', 'except', 'finally']:
                        if stripped.startswith(keyword + ' ') or stripped == keyword:
                            fixed_lines[line_num] = line.rstrip() + ':'
                            fixed = '\n'.join(fixed_lines)
                            warnings.append(f"✓ Fixed: Added missing colon at line {e.lineno}")
                            try:
                                compile(fixed, script_name, "exec")
                                syntax_errors = []  # Fixed!
                                break
                            except SyntaxError:
                                pass  # Try next fix
        
        # If still has errors, try one more compile to see if we fixed it
        if syntax_errors:
            try:
                compile(fixed, script_name, "exec")
                syntax_errors = []
                warnings.append("✓ Fixed: Syntax error resolved after corrections.")
            except SyntaxError as e2:
                warnings.append(f"⚠ Could not auto-fix syntax error. Manual review needed at line {e2.lineno}.")
    
    # ========== PHASE 5: Fix line endings and ensure proper format ==========
    if fixed and not fixed.endswith('\n'):
        fixed += '\n'
        warnings.append("✓ Fixed: Added missing newline at end of file.")
    
    # ========== PHASE 6: Check for problematic patterns ==========
    # Warn about __file__ usage (may not work as expected in 4DLLM)
    if '__file__' in fixed:
        warnings.append("ℹ Note: Script uses __file__ - may reference injected code in 4DLLM context.")
    
    # Warn about sys.path manipulation (may conflict with module system)
    if 'sys.path' in fixed and ('append' in fixed or 'insert' in fixed):
        warnings.append("ℹ Note: Script modifies sys.path - may conflict with 4DLLM module system.")
    
    # ========== PHASE 7: Final validation ==========
    try:
        # Final syntax check
        compile(fixed, script_name, "exec")
        
        # Check for basic AST validity
        try:
            ast.parse(fixed)
        except SyntaxError as e:
            warnings.append(f"⚠ AST parse error: {e.msg} at line {e.lineno}")
    except Exception as e:
        warnings.append(f"⚠ Final validation warning: {str(e)}")
    
    # Summary
    if warnings:
        fixed_count = sum(1 for w in warnings if w.startswith("✓"))
        warning_count = sum(1 for w in warnings if w.startswith("⚠"))
        info_count = sum(1 for w in warnings if w.startswith("ℹ"))
        if fixed_count > 0:
            warnings.insert(0, f"Auto-corrected {fixed_count} issue(s) in {script_name}")
    
    return fixed, warnings

# ---------------- Module Awareness Functions ----------------

def parse_imports_from_script(content: str) -> List[str]:
    """Parse all imports from a Python script using AST."""
    imports: set = set()
    try:
        tree = ast.parse(content, mode="exec")
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for alias in node.names:
                    imports.add(alias.name.split('.')[0])
            elif isinstance(node, ast.ImportFrom):
                if node.module:
                    imports.add(node.module.split('.')[0])
                    imports.add(node.module)
    except (SyntaxError, Exception):
        pass
    return sorted(list(imports))

def create_module_awareness_code(script_name: str, script_imports: List[str], all_available_modules: Dict[str, Any]) -> str:
    """Creates code that injects module awareness into a script."""
    modules_json = json.dumps(all_available_modules, indent=2)
    awareness_code = f'''
# ========== 4DLLM Module Awareness Injection ==========
__4dllm_script_name__ = {repr(script_name)}
__4dllm_script_imports__ = {repr(script_imports)}
__4dllm_available_modules__ = {modules_json}

def __4dllm_get_modules__():
    return {{
        'script_name': __4dllm_script_name__,
        'my_imports': __4dllm_script_imports__,
        'available_modules': __4dllm_available_modules__,
        'has_module': lambda name: name in __4dllm_available_modules__.get('all_modules', [])
    }}

def __4dllm_check_module__(module_name: str) -> bool:
    all_mods = __4dllm_available_modules__.get('all_modules', [])
    return module_name in all_mods or any(module_name in mod for mod in all_mods)

__modules__ = __4dllm_get_modules__()
# ========== End Module Awareness Injection ==========

'''
    return awareness_code

def inject_module_awareness(script, all_modules: Dict[str, Any]) -> str:
    """Injects module awareness code into a script."""
    awareness_code = create_module_awareness_code(script.name, script.imports, all_modules)
    content = script.content
    lines = content.split('\n')
    future_imports_end = 0
    seen_first_non_blank = False
    in_future_imports = False
    docstring_open = False
    docstring_type = None
    
    for i, line in enumerate(lines):
        stripped = line.strip()
        if i == 0 and stripped.startswith('#!'):
            future_imports_end = i + 1
            continue
        if not docstring_open:
            if stripped.startswith('"""') or stripped.startswith("'''"):
                docstring_type = '"""' if stripped.startswith('"""') else "'''"
                docstring_open = True
                if stripped.count(docstring_type) >= 2:
                    docstring_open = False
                continue
        else:
            if docstring_type in stripped:
                docstring_open = False
            continue
        if not stripped:
            if not in_future_imports:
                continue
            continue
        if stripped.startswith('#') and not in_future_imports:
            continue
        if stripped.startswith('from __future__'):
            in_future_imports = True
            seen_first_non_blank = True
            future_imports_end = i + 1
            continue
        if in_future_imports:
            if stripped.startswith('#'):
                future_imports_end = i + 1
                continue
            break
        if seen_first_non_blank:
            break
        if not in_future_imports:
            seen_first_non_blank = True
            break
    
    if future_imports_end > 0:
        before = '\n'.join(lines[:future_imports_end])
        after = '\n'.join(lines[future_imports_end:])
        return before + '\n\n' + awareness_code + '\n' + after
    else:
        return awareness_code + '\n' + content

# ---------------- Builder Classes ----------------

@dataclass
class ScriptItem:
    name: str
    content: str
    description: str = ""
    priority: int = 0
    enabled: bool = True
    source_path: str = ""
    imports: List[str] = None
    injected_content: str = ""
    
    def __post_init__(self):
        if self.imports is None:
            self.imports = []
        if not self.injected_content:
            self.injected_content = self.content

@dataclass
class SectionRecord:
    section_type: int
    flags: int
    offset: int
    size_c: int
    size_u: int
    extra_sz: int
    crc32: int
    sha256: Optional[str] = None

class FourDLLMBuilderV2:
    """Builder that writes a runner-friendly .4dllm (v2) with a footer TOC."""
    
    def __init__(self, gguf_file: str):
        self.gguf_file = Path(gguf_file)
        if not self.gguf_file.exists():
            raise FileNotFoundError(f"GGUF file not found: {gguf_file}")
        self.scripts: List[ScriptItem] = []
        self.metadata: Dict[str, Any] = {}
        self.module_registry: Dict[str, Any] = {}
    
    def add_script(self, item: ScriptItem) -> None:
        if not item.imports:
            item.imports = parse_imports_from_script(item.content)
        self.scripts.append(item)
    
    def _build_module_registry(self) -> Dict[str, Any]:
        """Builds a comprehensive registry of all modules used across all scripts."""
        all_modules: set = set()
        script_modules: Dict[str, List[str]] = {}
        for script in self.scripts:
            if not script.enabled:
                continue
            if not script.imports:
                script.imports = parse_imports_from_script(script.content)
            script_modules[script.name] = script.imports
            all_modules.update(script.imports)
        return {
            "all_modules": sorted(list(all_modules)),
            "modules_by_script": script_modules,
            "module_count": len(all_modules),
            "script_count": len(script_modules),
        }
    
    def build(self, output_path: str, compress_scripts: bool = True, compress_metadata: bool = True) -> str:
        out = Path(output_path)
        tmp_out = out.with_suffix(out.suffix + ".tmp")
        self.scripts.sort(key=lambda s: (s.priority, s.name), reverse=True)
        self.module_registry = self._build_module_registry()
        for script in self.scripts:
            if script.enabled:
                script.injected_content = inject_module_awareness(script, self.module_registry)
        sections: List[SectionRecord] = []
        
        with open(tmp_out, "wb") as f:
            f.write(FOURDLLM_MAGIC)
            f.write(struct.pack("<I", FOURDLLM_VERSION))
            f.write(b"\x00" * (FOURDLLM_HEADER_SIZE - 8))
            
            # GGUF Section
            gguf_extra = {"name": self.gguf_file.name}
            sec = self._write_section_stream_file(f=f, section_type=SECTION_GGUF_DATA, file_path=self.gguf_file, compress=False, extra_data=gguf_extra)
            sections.append(sec)
            
            # Metadata section
            if not self.metadata:
                self.metadata = {}
            self.metadata.setdefault("built_at", utc_iso())
            self.metadata.setdefault("builder_version", "4dllm_builder_v2")
            self.metadata.setdefault("fourdllm_version", FOURDLLM_VERSION)
            self.metadata.setdefault("modules", self.module_registry)
            meta_bytes = json.dumps(self.metadata, indent=2, sort_keys=True).encode("utf-8")
            sec = self._write_section_bytes(f=f, section_type=SECTION_METADATA, data=meta_bytes, compress=compress_metadata, extra_data={"kind": "metadata"})
            sections.append(sec)
            
            # Script config section
            enabled_scripts = [s for s in self.scripts if s.enabled]
            script_config = {
                "script_count": len(enabled_scripts),
                "scripts": [{"name": s.name, "priority": s.priority, "enabled": True, "source_path": s.source_path, "description": s.description, "imports": s.imports} for s in enabled_scripts],
                "modules": self.module_registry,
                "hooks": {"pre_prompt": [], "post_prompt": [], "tool_call": []},
                "runner_contract": {"context_keys_min": ["input", "meta"], "script_outputs": "script may set `result` dict; runner may read it", "module_awareness": "scripts have __modules__ variable and __4dllm_get_modules__() function"},
            }
            cfg_bytes = json.dumps(script_config, indent=2, sort_keys=True).encode("utf-8")
            sec = self._write_section_bytes(f=f, section_type=SECTION_SCRIPT_CONFIG, data=cfg_bytes, compress=compress_metadata, extra_data={"kind": "script_config"})
            sections.append(sec)
            
            # Script sections
            for s in self.scripts:
                if not s.enabled:
                    continue
                script_bytes = s.injected_content.encode("utf-8")
                sec = self._write_section_bytes(f=f, section_type=SECTION_PYTHON_SCRIPT, data=script_bytes, compress=compress_scripts, extra_data={"name": s.name, "priority": s.priority, "imports": s.imports})
                sections.append(sec)
            
            # Manifest section
            manifest = {
                "schema_version": "1.0", "fourdllm_version": FOURDLLM_VERSION,
                "features": {"crc32": True, "sha256": True, "toc_footer": True, "compressed_zlib": True},
                "sections": [{"type": s.section_type, "flags": s.flags, "size_c": s.size_c, "size_u": s.size_u, "extra_sz": s.extra_sz, "crc32": s.crc32, "sha256": s.sha256} for s in sections],
            }
            manifest_bytes = json.dumps(manifest, indent=2, sort_keys=True).encode("utf-8")
            sec = self._write_section_bytes(f=f, section_type=SECTION_MANIFEST, data=manifest_bytes, compress=False, extra_data={"kind": "manifest", "schema": "1.0"})
            sections.append(sec)
            
            # Footer TOC
            self._write_toc(f, sections)
        
        tmp_out.replace(out)
        return str(out)
    
    def _write_section_bytes(self, f, section_type: int, data: bytes, compress: bool, extra_data: Optional[Dict[str, Any]] = None) -> SectionRecord:
        offset = f.tell()
        extra_bytes = b""
        if extra_data:
            extra_bytes = json.dumps(extra_data, separators=(",", ":"), sort_keys=True).encode("utf-8")
        extra_sz = len(extra_bytes)
        crc = zlib.crc32(data) & 0xFFFFFFFF
        sha256 = _sha256_bytes(data)
        size_u = len(data)
        flags = 0
        payload = data
        if compress:
            payload = zlib.compress(data, level=9)
            flags |= FLAG_COMPRESSED_ZLIB
        size_c = len(payload)
        f.write(SECTION_HDR_STRUCT.pack(section_type, flags, size_c, size_u, extra_sz, crc))
        if extra_sz:
            f.write(extra_bytes)
        f.write(payload)
        return SectionRecord(section_type, flags, offset, size_c, size_u, extra_sz, crc, sha256)
    
    def _write_section_stream_file(self, f, section_type: int, file_path: Path, compress: bool, extra_data: Optional[Dict[str, Any]] = None, chunk_size: int = 1024 * 1024) -> SectionRecord:
        if compress:
            raise ValueError("Streaming file compression not supported (intentional). Keep GGUF raw.")
        offset = f.tell()
        extra_bytes = b""
        if extra_data:
            extra_bytes = json.dumps(extra_data, separators=(",", ":"), sort_keys=True).encode("utf-8")
        extra_sz = len(extra_bytes)
        crc = 0
        size_u = 0
        hdr_pos = f.tell()
        f.write(b"\x00" * SECTION_HDR_STRUCT.size)
        if extra_sz:
            f.write(extra_bytes)
        payload_start = f.tell()
        with open(file_path, "rb") as rf:
            while True:
                chunk = rf.read(chunk_size)
                if not chunk:
                    break
                size_u += len(chunk)
                crc = zlib.crc32(chunk, crc)
                f.write(chunk)
        payload_end = f.tell()
        size_c = payload_end - payload_start
        flags = 0
        crc &= 0xFFFFFFFF
        cur = f.tell()
        f.seek(hdr_pos)
        f.write(SECTION_HDR_STRUCT.pack(section_type, flags, size_c, size_u, extra_sz, crc))
        f.seek(cur)
        return SectionRecord(section_type, flags, offset, size_c, size_u, extra_sz, crc)
    
    def _write_toc(self, f, sections: List[SectionRecord]) -> None:
        toc_start = f.tell()
        f.write(TOC_HDR_STRUCT.pack(TOC_MAGIC, TOC_VERSION, len(sections)))
        toc_bytes_for_crc = bytearray()
        toc_bytes_for_crc += TOC_HDR_STRUCT.pack(TOC_MAGIC, TOC_VERSION, len(sections))
        for s in sections:
            entry = TOC_ENTRY_STRUCT.pack(s.section_type, s.flags, s.offset, s.size_c, s.size_u, s.extra_sz, s.crc32)
            f.write(entry)
            toc_bytes_for_crc += entry
        footer_crc = zlib.crc32(toc_bytes_for_crc) & 0xFFFFFFFF
        f.write(struct.pack("<I", footer_crc))
        DEBUG_LOGGER.info("Wrote TOC footer at offset %d (crc32=%08x)", toc_start, footer_crc)

# ---------------- Post-Build Validation ----------------

def _val_read_exact(f, n: int) -> bytes:
    b = f.read(n)
    if len(b) != n:
        raise EOFError(f"Expected {n} bytes, got {len(b)}")
    return b

def _val_crc32_bytes(b: bytes) -> int:
    return zlib.crc32(b) & 0xFFFFFFFF

def _val_safe_json_loads(b: bytes, default: Any) -> Any:
    try:
        return json.loads(b.decode("utf-8", errors="replace"))
    except Exception:
        return default

def _find_toc_offset(path: Path, scan_back_bytes: int = 4 * 1024 * 1024) -> int:
    size = path.stat().st_size
    start = max(0, size - scan_back_bytes)
    with open(path, "rb") as f:
        f.seek(start)
        chunk = f.read(size - start)
    idx = chunk.rfind(TOC_MAGIC)
    if idx < 0:
        raise ValueError("TOC not found (TOC1). File may not be a v2 4DLLM.")
    return start + idx

@dataclass
class _SmallSection:
    entry: SectionRecord
    extra: Dict[str, Any]
    payload_c: bytes
    payload_u: Optional[bytes] = None

def _read_and_validate_toc(path: Path) -> Tuple[List[SectionRecord], int]:
    toc_offset = _find_toc_offset(path)
    with open(path, "rb") as f:
        f.seek(toc_offset)
        hdr = _val_read_exact(f, TOC_HDR_STRUCT.size)
        magic, toc_ver, count = TOC_HDR_STRUCT.unpack(hdr)
        if magic != TOC_MAGIC:
            raise ValueError("Bad TOC magic")
        if toc_ver != TOC_VERSION:
            raise ValueError(f"Unsupported TOC version: {toc_ver}")
        entries_bytes = _val_read_exact(f, TOC_ENTRY_STRUCT.size * count)
        footer_crc = struct.unpack("<I", _val_read_exact(f, 4))[0]
        computed = _val_crc32_bytes(hdr + entries_bytes)
        if computed != footer_crc:
            raise ValueError(f"TOC CRC mismatch: expected {footer_crc:08x}, got {computed:08x}")
        entries: List[SectionRecord] = []
        for i in range(count):
            chunk = entries_bytes[i * TOC_ENTRY_STRUCT.size:(i + 1) * TOC_ENTRY_STRUCT.size]
            stype, flags, off, size_c, size_u, extra_sz, crc_u = TOC_ENTRY_STRUCT.unpack(chunk)
            entries.append(SectionRecord(stype, flags, off, size_c, size_u, extra_sz, crc_u))
        return entries, toc_offset

def _read_section_small(path: Path, entry: SectionRecord) -> _SmallSection:
    with open(path, "rb") as f:
        f.seek(entry.offset)
        hdr = _val_read_exact(f, SECTION_HDR_STRUCT.size)
        stype, flags, size_c, size_u, extra_sz, crc_u = SECTION_HDR_STRUCT.unpack(hdr)
        if stype != entry.section_type or flags != entry.flags:
            raise ValueError("Section header mismatch vs TOC (type/flags).")
        if size_c != entry.size_c or size_u != entry.size_u:
            raise ValueError("Section header mismatch vs TOC (sizes).")
        if extra_sz != entry.extra_sz or crc_u != entry.crc32:
            raise ValueError("Section header mismatch vs TOC (extra/crc).")
        extra_bytes = _val_read_exact(f, extra_sz) if extra_sz else b"{}"
        extra = _val_safe_json_loads(extra_bytes, default={})
        payload_c = _val_read_exact(f, size_c) if size_c else b""
        return _SmallSection(entry=entry, extra=extra, payload_c=payload_c)

def _decode_and_validate_section(section: _SmallSection) -> bytes:
    if section.payload_u is not None:
        return section.payload_u
    data = section.payload_c
    if section.entry.flags & FLAG_COMPRESSED_ZLIB:
        data = zlib.decompress(data)
    c = zlib.crc32(data) & 0xFFFFFFFF
    if c != section.entry.crc32:
        raise ValueError(f"CRC mismatch for section {section.entry.section_type:#x}: expected {section.entry.crc32:08x}, got {c:08x}")
    if len(data) != section.entry.size_u:
        raise ValueError(f"Size_u mismatch after decode: expected {section.entry.size_u}, got {len(data)}")
    section.payload_u = data
    return data

def validate_built_file(path: Path) -> Dict[str, Any]:
    """Lightweight validation to ensure the freshly built file will load in the runner."""
    with open(path, "rb") as f:
        magic = _val_read_exact(f, 4)
        if magic != FOURDLLM_MAGIC:
            raise ValueError("Not a 4DLLM file (bad magic).")
        ver = struct.unpack("<I", _val_read_exact(f, 4))[0]
        if ver != FOURDLLM_VERSION:
            raise ValueError(f"Unsupported 4DLLM version: {ver} (expected {FOURDLLM_VERSION})")
    entries, toc_offset = _read_and_validate_toc(path)
    gguf_entries = [e for e in entries if e.section_type == SECTION_GGUF_DATA]
    if not gguf_entries:
        raise ValueError("No GGUF section found.")
    metadata: Dict[str, Any] = {}
    script_config: Dict[str, Any] = {}
    script_sections = 0
    for ent in entries:
        if ent.section_type == SECTION_METADATA:
            sec = _read_section_small(path, ent)
            metadata = _val_safe_json_loads(_decode_and_validate_section(sec), default={})
        elif ent.section_type == SECTION_SCRIPT_CONFIG:
            sec = _read_section_small(path, ent)
            script_config = _val_safe_json_loads(_decode_and_validate_section(sec), default={})
        elif ent.section_type == SECTION_PYTHON_SCRIPT:
            sec = _read_section_small(path, ent)
            _decode_and_validate_section(sec)
            script_sections += 1
        elif ent.section_type == SECTION_MANIFEST:
            sec = _read_section_small(path, ent)
            _val_safe_json_loads(_decode_and_validate_section(sec), default={})
    expected_scripts = script_config.get("script_count")
    if isinstance(expected_scripts, int) and expected_scripts != script_sections:
        raise ValueError(f"script_count mismatch: config={expected_scripts}, sections={script_sections}")
    return {
        "toc_offset": toc_offset,
        "section_count": len(entries),
        "script_sections": script_sections,
        "script_count_declared": expected_scripts,
        "metadata_keys": sorted(list(metadata.keys())),
        "script_config_keys": sorted(list(script_config.keys())),
    }

# Modules folder name (same as standalone builder)
MODULES_DIRNAME = "modules"

# Create builder_module namespace for compatibility
class BuilderModule:
    """Namespace for builder functions - provides compatibility layer"""
    FourDLLMBuilderV2 = FourDLLMBuilderV2
    ScriptItem = ScriptItem
    validate_and_fix_script = validate_and_fix_script
    parse_imports_from_script = parse_imports_from_script
    validate_built_file = validate_built_file

builder_module = BuilderModule()
DEBUG_LOGGER.info("Builder module embedded successfully - no external import needed")

# Import GGUF editor functionality
editor_path = Path(__file__).parent / "gguf-editor.py"
editor_module = None
if editor_path.exists():
    try:
        spec = importlib.util.spec_from_file_location("editor_module", str(editor_path))
        if spec and spec.loader:
            editor_module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(editor_module)
        DEBUG_LOGGER.info("Editor module loaded successfully")
    except Exception as e:
        DEBUG_LOGGER.error(f"Failed to load editor module: {e}")

# Import trainer functionality
trainer_path = Path(__file__).parent / "4dllm_trainer.py"
trainer_module = None
TRAINING_PRESETS = {
    "Quick Fine-Tune": {
        "epochs": "3",
        "learning_rate": "0.0001",
        "batch_size": "4",
        "context_size": "2048",
        "lora_r": "8",
        "lora_alpha": "16",
        "lora_dropout": "0.05",
    },
    "Standard Training": {
        "epochs": "5",
        "learning_rate": "0.0001",
        "batch_size": "8",
        "context_size": "4096",
        "lora_r": "16",
        "lora_alpha": "32",
        "lora_dropout": "0.1",
    },
    "Advanced Training": {
        "epochs": "10",
        "learning_rate": "0.00005",
        "batch_size": "16",
        "context_size": "8192",
        "lora_r": "32",
        "lora_alpha": "64",
        "lora_dropout": "0.15",
    },
}
if trainer_path.exists():
    try:
        spec = importlib.util.spec_from_file_location("trainer_module", str(trainer_path))
        if spec and spec.loader:
            trainer_module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(trainer_module)
            # Extract training presets if available
            if hasattr(trainer_module, 'TRAINING_PRESETS'):
                TRAINING_PRESETS = trainer_module.TRAINING_PRESETS
        DEBUG_LOGGER.info("Trainer module loaded successfully")
    except Exception as e:
        DEBUG_LOGGER.error(f"Failed to load trainer module: {e}")

if gui_module and hasattr(gui_module, 'read_4dllm_info'):
    read_4dllm_info = gui_module.read_4dllm_info
else:
    def read_4dllm_info(path):
        return {"valid": False, "error": "Import failed"}

if runner_module:
    read_and_validate_toc = runner_module.read_and_validate_toc
    read_section_small = runner_module.read_section_small
    SECTION_PYTHON_SCRIPT = runner_module.SECTION_PYTHON_SCRIPT
    SECTION_METADATA = runner_module.SECTION_METADATA
    SECTION_SCRIPT_CONFIG = runner_module.SECTION_SCRIPT_CONFIG
    # Get GGUF section type
    SECTION_GGUF_DATA = getattr(runner_module, 'SECTION_GGUF_DATA', 0x01)
else:
    def read_and_validate_toc(path):
        return [], None
    def read_section_small(path, entry):
        return None
    SECTION_PYTHON_SCRIPT = 0x02
    SECTION_METADATA = 0x03
    SECTION_SCRIPT_CONFIG = 0x04
    SECTION_GGUF_DATA = 0x01  # GGUF data section

# ---------------- World-Class HTML/CSS/JS Interface ----------------

HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>4DLLM Studio</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Inter', 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
            background: 
                radial-gradient(ellipse at top, rgba(6, 182, 212, 0.1) 0%, transparent 50%),
                radial-gradient(ellipse at bottom right, rgba(59, 130, 246, 0.08) 0%, transparent 50%),
                linear-gradient(135deg, #0a0f1a 0%, #1a1f2e 100%);
            background-attachment: fixed;
            color: #e2e8f0;
            overflow: hidden;
            height: 100vh;
            width: 100vw;
        }
        
        .app-container {
            display: flex;
            flex-direction: column;
            height: 100vh;
            width: 100vw;
        }
        
        /* World-Class Header Banner */
        .header {
            background: linear-gradient(135deg, #0a0f1a 0%, #1e293b 30%, #334155 70%, #1e293b 100%);
            padding: 24px 40px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1);
            border-bottom: 3px solid transparent;
            background-image: 
                linear-gradient(135deg, #0a0f1a 0%, #1e293b 30%, #334155 70%, #1e293b 100%),
                linear-gradient(90deg, transparent 0%, #06b6d4 20%, #22d3ee 50%, #06b6d4 80%, transparent 100%);
            background-size: 100% 100%, 200% 3px;
            background-position: center, bottom;
            background-repeat: no-repeat, repeat-x;
            position: relative;
            overflow: hidden;
        }
        
        .header-code-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 0;
            overflow: hidden;
        }
        
        .code-easter-egg {
            position: absolute;
            right: -100px;
            top: 50%;
            transform: translateY(-50%) rotate(-10deg);
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 64px;
            font-weight: 800;
            white-space: nowrap;
            line-height: 1.5;
            letter-spacing: 4px;
            user-select: none;
            animation: codeGlow 8s ease-in-out infinite;
        }
        
        .code-easter-egg .code-line {
            display: block;
            margin-bottom: 16px;
            font-size: 58px;
            text-shadow: 0 0 30px rgba(6, 182, 212, 0.5), 0 0 60px rgba(34, 211, 238, 0.3);
        }
        
        .code-easter-egg .code-line:nth-child(1) {
            color: rgba(6, 182, 212, 0.22);
            animation: codeLinePulse 6s ease-in-out infinite;
        }
        
        .code-easter-egg .code-line:nth-child(2) {
            color: rgba(34, 211, 238, 0.20);
            animation: codeLinePulse 6s ease-in-out infinite 0.8s;
        }
        
        @keyframes codeGlow {
            0%, 100% {
                opacity: 0.18;
            }
            50% {
                opacity: 0.26;
            }
        }
        
        @keyframes codeLinePulse {
            0%, 100% {
                text-shadow: 0 0 30px rgba(6, 182, 212, 0.4), 0 0 60px rgba(34, 211, 238, 0.2);
            }
            50% {
                text-shadow: 0 0 45px rgba(34, 211, 238, 0.6), 0 0 90px rgba(6, 182, 212, 0.4);
            }
        }
        
        .header::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, 
                transparent 0%, 
                rgba(6, 182, 212, 0.1) 50%, 
                transparent 100%);
            animation: shimmer 4s infinite;
        }
        
        @keyframes shimmer {
            0% { left: -100%; }
            100% { left: 100%; }
        }
        
        .header-content {
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: relative;
            z-index: 1;
        }
        
        .logo-container {
            display: flex;
            align-items: center;
            gap: 20px;
        }
        
        .logo-img {
            width: 101px;
            height: 101px;
            object-fit: contain;
            filter: drop-shadow(0 0 25px rgba(6, 182, 212, 0.7));
            animation: logoGlow 3s ease-in-out infinite;
            transition: transform 0.3s;
        }
        
        .logo-img:hover {
            transform: scale(1.05) rotate(5deg);
        }
        
        .logo-svg {
            width: 72px;
            height: 72px;
            filter: drop-shadow(0 0 25px rgba(6, 182, 212, 0.7));
            animation: logoGlow 3s ease-in-out infinite;
            transition: transform 0.3s;
            display: none !important;
        }
        
        @keyframes logoGlow {
            0%, 100% { 
                filter: drop-shadow(0 0 25px rgba(6, 182, 212, 0.7)) 
                        drop-shadow(0 0 10px rgba(34, 211, 238, 0.5)); 
            }
            50% { 
                filter: drop-shadow(0 0 35px rgba(34, 211, 238, 1)) 
                        drop-shadow(0 0 15px rgba(6, 182, 212, 0.8)); 
            }
        }
        
        .logo-text-container {
            display: flex;
            flex-direction: column;
            gap: 4px;
        }
        
        .logo-main-text {
            font-size: 42px;
            font-weight: 900;
            background: linear-gradient(135deg, #06b6d4 0%, #22d3ee 40%, #3b82f6 60%, #22d3ee 80%, #06b6d4 100%);
            background-size: 200% 200%;
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            letter-spacing: -1px;
            text-shadow: 0 0 50px rgba(6, 182, 212, 0.5);
            line-height: 1.1;
            animation: gradientShift 4s ease infinite;
        }
        
        @keyframes gradientShift {
            0%, 100% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
        }
        
        .logo-brand-text {
            font-size: 16px;
            font-weight: 600;
            color: #cbd5e1;
            letter-spacing: 1px;
            margin-top: 2px;
            text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
        }
        
        .logo-email {
            font-size: 14px;
            color: #94a3b8;
            font-weight: 500;
            margin-top: 6px;
            transition: all 0.3s;
            cursor: pointer;
            display: inline-block;
        }
        
        .logo-email:hover {
            color: #22d3ee;
            text-shadow: 0 0 10px rgba(34, 211, 238, 0.5);
            transform: translateX(2px);
        }
        
        /* Integrated Navigation in Sidebar */
        .nav-tabs {
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-top: 20px;
        }
        
        .nav-tab {
            width: 100%;
            padding: 12px 16px;
            background: rgba(51, 65, 85, 0.4);
            border: 1px solid rgba(71, 85, 105, 0.3);
            color: #cbd5e1;
            font-size: 13px;
            font-weight: 600;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            position: relative;
            overflow: hidden;
            text-align: left;
        }
        
        .nav-tab::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(6, 182, 212, 0.2), transparent);
            transition: left 0.5s;
        }
        
        .nav-tab:hover::before {
            left: 100%;
        }
        
        .nav-tab:hover {
            background: rgba(6, 182, 212, 0.2);
            border-color: rgba(6, 182, 212, 0.5);
            color: #22d3ee;
            transform: translateX(2px);
            box-shadow: 0 2px 8px rgba(6, 182, 212, 0.3);
        }
        
        .nav-tab.active {
            background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #06b6d4 100%);
            background-size: 200% 200%;
            border-color: rgba(59, 130, 246, 0.6);
            color: #ffffff;
            box-shadow: 
                0 4px 12px rgba(59, 130, 246, 0.5),
                inset 0 1px 0 rgba(255, 255, 255, 0.25),
                0 0 20px rgba(59, 130, 246, 0.2);
            transform: translateX(2px);
            animation: activeTabGlow 3s ease-in-out infinite;
        }
        
        @keyframes activeTabGlow {
            0%, 100% { 
                background-position: 0% 50%;
                box-shadow: 
                    0 4px 12px rgba(59, 130, 246, 0.5),
                    inset 0 1px 0 rgba(255, 255, 255, 0.25),
                    0 0 20px rgba(59, 130, 246, 0.2);
            }
            50% { 
                background-position: 100% 50%;
                box-shadow: 
                    0 4px 12px rgba(59, 130, 246, 0.6),
                    inset 0 1px 0 rgba(255, 255, 255, 0.3),
                    0 0 25px rgba(6, 182, 212, 0.3);
            }
        }
        
        /* Main Content Area */
        .main-content {
            display: flex;
            flex: 1;
            overflow: hidden;
            gap: 12px;
            padding: 12px;
        }
        
        .sidebar {
            width: 280px;
            background: 
                linear-gradient(180deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%),
                linear-gradient(135deg, rgba(6, 182, 212, 0.05) 0%, transparent 50%);
            border-radius: 16px;
            padding: 20px;
            box-shadow: 
                0 8px 32px rgba(0, 0, 0, 0.4),
                0 0 0 1px rgba(71, 85, 105, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(71, 85, 105, 0.3);
            backdrop-filter: blur(20px) saturate(180%);
            overflow-y: auto;
            position: relative;
        }
        
        .sidebar::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 1px;
            background: linear-gradient(90deg, 
                transparent 0%, 
                rgba(6, 182, 212, 0.3) 50%, 
                transparent 100%);
            pointer-events: none;
        }
        
        .sidebar-section {
            margin-bottom: 24px;
        }
        
        .section-title {
            font-size: 12px;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 1.5px;
            color: #06b6d4;
            margin-bottom: 12px;
            text-shadow: 0 0 10px rgba(6, 182, 212, 0.3);
            position: relative;
            padding-left: 8px;
        }
        
        .section-title::before {
            content: '';
            position: absolute;
            left: 0;
            top: 50%;
            transform: translateY(-50%);
            width: 3px;
            height: 60%;
            background: linear-gradient(180deg, #06b6d4 0%, #22d3ee 100%);
            border-radius: 2px;
            box-shadow: 0 0 8px rgba(6, 182, 212, 0.5);
        }
        
        .btn-primary {
            width: 100%;
            padding: 12px 20px;
            background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #06b6d4 100%);
            background-size: 200% 200%;
            border: none;
            border-radius: 12px;
            color: #ffffff;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 
                0 4px 15px rgba(59, 130, 246, 0.4),
                0 0 0 1px rgba(255, 255, 255, 0.1) inset,
                0 2px 4px rgba(0, 0, 0, 0.2);
            margin-bottom: 8px;
            position: relative;
            overflow: hidden;
            letter-spacing: 0.3px;
        }
        
        .btn-primary::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 0;
            height: 0;
            border-radius: 50%;
            background: radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, transparent 70%);
            transform: translate(-50%, -50%);
            transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1), height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        .btn-primary::after {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.5s;
        }
        
        .btn-primary:hover::before {
            width: 300px;
            height: 300px;
        }
        
        .btn-primary:hover::after {
            left: 100%;
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            background-position: 100% 50%;
            box-shadow: 
                0 8px 25px rgba(59, 130, 246, 0.6),
                0 0 0 1px rgba(255, 255, 255, 0.2) inset,
                0 4px 8px rgba(0, 0, 0, 0.3);
        }
        
        .btn-primary:active {
            transform: translateY(0);
            box-shadow: 
                0 2px 8px rgba(59, 130, 246, 0.4),
                0 0 0 1px rgba(255, 255, 255, 0.1) inset;
        }
        
        .btn-secondary {
            width: 100%;
            padding: 10px 18px;
            background: 
                linear-gradient(135deg, rgba(51, 65, 85, 0.6) 0%, rgba(71, 85, 105, 0.6) 100%);
            border: 1px solid rgba(71, 85, 105, 0.5);
            border-radius: 10px;
            color: #e2e8f0;
            font-size: 12px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            margin-bottom: 8px;
            position: relative;
            overflow: hidden;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
        }
        
        .btn-secondary::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(6, 182, 212, 0.2), transparent);
            transition: left 0.4s;
        }
        
        .btn-secondary:hover::before {
            left: 100%;
        }
        
        .btn-secondary:hover {
            background: 
                linear-gradient(135deg, rgba(71, 85, 105, 0.8) 0%, rgba(100, 116, 139, 0.8) 100%);
            border-color: #06b6d4;
            transform: translateY(-1px);
            box-shadow: 
                0 4px 12px rgba(6, 182, 212, 0.3),
                0 0 0 1px rgba(6, 182, 212, 0.2);
        }
        
        .btn-secondary:active {
            transform: translateY(0);
        }
        
        .workspace {
            flex: 1;
            background: 
                linear-gradient(180deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%),
                linear-gradient(135deg, rgba(6, 182, 212, 0.03) 0%, transparent 50%);
            border-radius: 16px;
            padding: 24px;
            box-shadow: 
                0 8px 32px rgba(0, 0, 0, 0.4),
                0 0 0 1px rgba(71, 85, 105, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(71, 85, 105, 0.3);
            backdrop-filter: blur(20px) saturate(180%);
            overflow: hidden;
            display: flex;
            flex-direction: column;
            position: relative;
        }
        
        .workspace::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 1px;
            background: linear-gradient(90deg, 
                transparent 0%, 
                rgba(6, 182, 212, 0.3) 50%, 
                transparent 100%);
            pointer-events: none;
        }
        
        .tab-content {
            display: none;
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            opacity: 0;
            transform: translateY(10px);
            transition: opacity 0.3s ease, transform 0.3s ease;
        }
        
        .tab-content.active {
            display: flex;
            flex-direction: column;
            opacity: 1;
            transform: translateY(0);
            animation: fadeInUp 0.4s ease-out;
        }
        
        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(15px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        .content-header {
            font-size: 24px;
            font-weight: 700;
            background: linear-gradient(135deg, #06b6d4 0%, #22d3ee 50%, #3b82f6 100%);
            background-size: 200% 200%;
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            margin-bottom: 24px;
            animation: headerGradientShift 4s ease infinite;
            text-shadow: 0 0 30px rgba(6, 182, 212, 0.3);
            letter-spacing: -0.5px;
        }
        
        @keyframes headerGradientShift {
            0%, 100% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
        }
        
        .terminal-container {
            flex: 1;
            background: 
                radial-gradient(ellipse at center, rgba(6, 182, 212, 0.03) 0%, transparent 70%),
                #0a0f1a;
            border-radius: 12px;
            padding: 16px;
            border: 1px solid rgba(71, 85, 105, 0.4);
            box-shadow: 
                inset 0 2px 4px rgba(0, 0, 0, 0.3),
                0 0 0 1px rgba(6, 182, 212, 0.1);
            display: flex;
            flex-direction: column;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 13px;
            overflow: hidden;
            position: relative;
        }
        
        .terminal-overlay-logo {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 288px;
            height: 288px;
            pointer-events: none;
            z-index: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0;
            padding: 0;
        }
        
        .terminal-overlay-logo .logo-main {
            position: relative;
            width: 288px;
            height: 288px;
            object-fit: contain;
            opacity: 0.15;
            filter: drop-shadow(0 0 20px rgba(6, 182, 212, 0.4)) drop-shadow(0 0 40px rgba(34, 211, 238, 0.2)) brightness(1.1);
            animation: logoPulse 6s ease-in-out infinite;
            margin: auto;
        }
        
        .terminal-overlay-logo img {
            position: relative;
            width: 288px;
            height: 288px;
            object-fit: contain;
            opacity: 0.25;
            filter: drop-shadow(0 0 20px rgba(6, 182, 212, 0.4)) drop-shadow(0 0 40px rgba(34, 211, 238, 0.2)) brightness(1.1);
            animation: logoPulse 6s ease-in-out infinite;
            display: block !important;
            margin: 0 auto;
        }
        
        @keyframes logoPulse {
            0%, 100% { 
                transform: scale(1);
                opacity: 0.20;
                filter: drop-shadow(0 0 20px rgba(6, 182, 212, 0.2));
            }
            50% { 
                transform: scale(1.03);
                opacity: 0.30;
                filter: drop-shadow(0 0 30px rgba(34, 211, 238, 0.4));
            }
        }
        
        .terminal-output {
            flex: 1;
            background: transparent;
            color: #e2e8f0;
            overflow-y: auto;
            padding: 12px;
            line-height: 1.6;
            white-space: pre-wrap;
            word-wrap: break-word;
            position: relative;
            z-index: 2;
        }
        
        .terminal-output .info {
            color: #60a5fa;
        }
        
        .terminal-output .success {
            color: #34d399;
        }
        
        .terminal-output .error {
            color: #f87171;
        }
        
        .terminal-output .warning {
            color: #fbbf24;
        }
        
        .terminal-output .debug {
            color: #94a3b8;
        }
        
        .chat-input-container {
            margin-top: 12px;
            display: flex;
            gap: 8px;
        }
        
        .chat-input {
            flex: 1;
            background: 
                linear-gradient(135deg, rgba(15, 23, 42, 0.9) 0%, rgba(30, 41, 59, 0.9) 100%);
            border: 1px solid rgba(71, 85, 105, 0.5);
            border-radius: 10px;
            padding: 12px 16px;
            color: #e2e8f0;
            font-family: 'Consolas', monospace;
            font-size: 13px;
            resize: none;
            min-height: 60px;
            max-height: 120px;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        .chat-input:focus {
            outline: none;
            border-color: #06b6d4;
            box-shadow: 
                0 0 0 3px rgba(6, 182, 212, 0.15),
                inset 0 2px 4px rgba(0, 0, 0, 0.2),
                0 0 20px rgba(6, 182, 212, 0.1);
            background: 
                linear-gradient(135deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%);
        }
        
        .chat-input::placeholder {
            color: #64748b;
            opacity: 0.7;
        }
        
        .btn-send {
            padding: 12px 24px;
            background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #06b6d4 100%);
            background-size: 200% 200%;
            border: none;
            border-radius: 10px;
            color: #ffffff;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 
                0 4px 12px rgba(59, 130, 246, 0.4),
                0 0 0 1px rgba(255, 255, 255, 0.1) inset;
            position: relative;
            overflow: hidden;
        }
        
        .btn-send::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
            transition: left 0.5s;
        }
        
        .btn-send:hover::before {
            left: 100%;
        }
        
        .btn-send:hover {
            transform: translateY(-2px);
            background-position: 100% 50%;
            box-shadow: 
                0 6px 20px rgba(59, 130, 246, 0.6),
                0 0 0 1px rgba(255, 255, 255, 0.2) inset;
        }
        
        .btn-send:active {
            transform: translateY(0);
        }
        
        .details-panel {
            width: 320px;
            background: 
                linear-gradient(180deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%),
                linear-gradient(135deg, rgba(6, 182, 212, 0.05) 0%, transparent 50%);
            border-radius: 16px;
            padding: 20px;
            box-shadow: 
                0 8px 32px rgba(0, 0, 0, 0.4),
                0 0 0 1px rgba(71, 85, 105, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(71, 85, 105, 0.3);
            backdrop-filter: blur(20px) saturate(180%);
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 16px;
            position: relative;
        }
        
        .details-panel::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 1px;
            background: linear-gradient(90deg, 
                transparent 0%, 
                rgba(6, 182, 212, 0.3) 50%, 
                transparent 100%);
            pointer-events: none;
        }
        
        .details-section {
            flex: 1;
            display: flex;
            flex-direction: column;
            min-height: 0;
        }
        
        .details-header {
            font-size: 16px;
            font-weight: 700;
            color: #06b6d4;
            margin-bottom: 12px;
            padding-bottom: 8px;
            border-bottom: 2px solid rgba(6, 182, 212, 0.3);
        }
        
        .details-content {
            flex: 1;
            color: #cbd5e1;
            font-size: 12px;
            line-height: 1.6;
            white-space: pre-wrap;
            overflow-y: auto;
            padding: 8px;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 8px;
            font-family: 'Consolas', 'Monaco', monospace;
        }
        
        .debug-log {
            flex: 1;
            color: #94a3b8;
            font-size: 11px;
            line-height: 1.5;
            white-space: pre-wrap;
            overflow-y: auto;
            padding: 8px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 8px;
            font-family: 'Consolas', 'Monaco', monospace;
            max-height: 100%;
        }
        
        .debug-log-entry {
            margin-bottom: 4px;
            padding: 2px 0;
        }
        
        .debug-log-entry.error {
            color: #f87171;
        }
        
        .debug-log-entry.warning {
            color: #fbbf24;
        }
        
        .debug-log-entry.info {
            color: #60a5fa;
        }
        
        .debug-log-entry.debug {
            color: #94a3b8;
        }
        
        /* Scrollbar Styling */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        
        ::-webkit-scrollbar-track {
            background: rgba(15, 23, 42, 0.5);
            border-radius: 4px;
        }
        
        ::-webkit-scrollbar-thumb {
            background: linear-gradient(180deg, #475569 0%, #64748b 100%);
            border-radius: 4px;
        }
        
        ::-webkit-scrollbar-thumb:hover {
            background: linear-gradient(180deg, #64748b 0%, #06b6d4 100%);
            box-shadow: 0 0 10px rgba(6, 182, 212, 0.5);
        }
        
        ::-webkit-scrollbar-thumb:active {
            background: linear-gradient(180deg, #06b6d4 0%, #22d3ee 100%);
        }
        
        /* Animations */
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        
        .fade-in {
            animation: fadeIn 0.3s ease-out;
        }
        
        /* Glass morphism effects */
        .glass {
            background: rgba(15, 23, 42, 0.6);
            backdrop-filter: blur(20px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        /* Enhanced input and select styling */
        input[type="text"],
        input[type="file"],
        select,
        textarea {
            background: 
                linear-gradient(135deg, rgba(15, 23, 42, 0.9) 0%, rgba(30, 41, 59, 0.9) 100%);
            border: 1px solid rgba(71, 85, 105, 0.5);
            border-radius: 8px;
            padding: 10px 12px;
            color: #e2e8f0;
            font-size: 13px;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        input[type="text"]:focus,
        input[type="file"]:focus,
        select:focus,
        textarea:focus {
            outline: none;
            border-color: #06b6d4;
            box-shadow: 
                0 0 0 3px rgba(6, 182, 212, 0.15),
                inset 0 2px 4px rgba(0, 0, 0, 0.2),
                0 0 15px rgba(6, 182, 212, 0.1);
            background: 
                linear-gradient(135deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%);
        }
        
        input[type="text"]:hover,
        select:hover,
        textarea:hover {
            border-color: rgba(71, 85, 105, 0.7);
            box-shadow: 
                inset 0 2px 4px rgba(0, 0, 0, 0.2),
                0 0 10px rgba(6, 182, 212, 0.05);
        }
        
        /* Checkbox styling */
        input[type="checkbox"] {
            width: 18px;
            height: 18px;
            cursor: pointer;
            accent-color: #06b6d4;
            filter: drop-shadow(0 0 4px rgba(6, 182, 212, 0.3));
        }
        
        input[type="checkbox"]:hover {
            filter: drop-shadow(0 0 6px rgba(6, 182, 212, 0.5));
        }
        
        /* File info boxes */
        div[id*="file"],
        div[id*="status"],
        div[id*="info"] {
            background: 
                linear-gradient(135deg, rgba(15, 23, 42, 0.7) 0%, rgba(30, 41, 59, 0.7) 100%);
            border: 1px solid rgba(71, 85, 105, 0.4);
            border-radius: 8px;
            padding: 12px;
            transition: all 0.3s ease;
            box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        div[id*="file"]:hover,
        div[id*="status"]:hover,
        div[id*="info"]:hover {
            border-color: rgba(6, 182, 212, 0.3);
            box-shadow: 
                inset 0 2px 4px rgba(0, 0, 0, 0.2),
                0 0 15px rgba(6, 182, 212, 0.1);
        }
        
        /* Settings Tab Buttons */
        .settings-tab-btn {
            padding: 10px 16px;
            background: rgba(51, 65, 85, 0.4);
            border: 1px solid rgba(71, 85, 105, 0.3);
            color: #cbd5e1;
            font-size: 13px;
            font-weight: 600;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            position: relative;
            overflow: hidden;
        }
        
        .settings-tab-btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(6, 182, 212, 0.2), transparent);
            transition: left 0.4s;
        }
        
        .settings-tab-btn:hover::before {
            left: 100%;
        }
        
        .settings-tab-btn:hover {
            background: rgba(6, 182, 212, 0.2);
            border-color: rgba(6, 182, 212, 0.5);
            color: #22d3ee;
        }
        
        .settings-tab-btn.active {
            background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #06b6d4 100%);
            border-color: rgba(59, 130, 246, 0.6);
            color: #ffffff;
            box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
        }
        
        /* Settings Panels */
        .settings-panel {
            display: none;
        }
        
        .settings-panel.active {
            display: block;
            animation: fadeInUp 0.3s ease-out;
        }
    </style>
</head>
<body>
    <div class="app-container">
        <!-- World-Class Header Banner -->
        <div class="header">
            <div class="header-code-overlay">
                <div class="code-easter-egg">
                    <span class="code-line">def hypercube_4d():</span>
                    <span class="code-line">class FourDLLM:</span>
                </div>
            </div>
            <div class="header-content">
                <div class="logo-container">
                    <img src="HEADER_LOGO_PLACEHOLDER" alt="RomanAILabs Logo" class="logo-img" style="width: 101px; height: 101px; object-fit: contain;" onerror="console.error('Failed to load header logo:', this.src);">
                    <svg class="logo-svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" style="display: none !important;">
                        <defs>
                            <linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
                                <stop offset="0%" style="stop-color:#06b6d4;stop-opacity:1" />
                                <stop offset="50%" style="stop-color:#22d3ee;stop-opacity:1" />
                                <stop offset="100%" style="stop-color:#06b6d4;stop-opacity:1" />
                            </linearGradient>
                            <linearGradient id="logoGradient2" x1="0%" y1="0%" x2="100%" y2="100%">
                                <stop offset="0%" style="stop-color:#3b82f6;stop-opacity:0.8" />
                                <stop offset="100%" style="stop-color:#06b6d4;stop-opacity:0.8" />
                            </linearGradient>
                            <filter id="glow">
                                <feGaussianBlur stdDeviation="4" result="coloredBlur"/>
                                <feMerge>
                                    <feMergeNode in="coloredBlur"/>
                                    <feMergeNode in="SourceGraphic"/>
                                </feMerge>
                            </filter>
                            <filter id="shadow">
                                <feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
                                <feOffset dx="2" dy="2" result="offsetblur"/>
                                <feComponentTransfer>
                                    <feFuncA type="linear" slope="0.3"/>
                                </feComponentTransfer>
                                <feMerge>
                                    <feMergeNode/>
                                    <feMergeNode in="SourceGraphic"/>
                                </feMerge>
                            </filter>
                        </defs>
                        <!-- 4D Cube with 3D effect -->
                        <g filter="url(#shadow)">
                            <!-- Main cube -->
                            <rect x="20" y="20" width="60" height="60" rx="10" fill="url(#logoGradient)" filter="url(#glow)" opacity="0.95"/>
                            <!-- Top face (3D effect) -->
                            <polygon points="20,20 30,10 90,10 80,20" fill="url(#logoGradient2)" opacity="0.7"/>
                            <!-- Right face (3D effect) -->
                            <polygon points="80,20 90,10 90,70 80,80" fill="url(#logoGradient2)" opacity="0.5"/>
                            <!-- Inner border -->
                            <rect x="25" y="25" width="50" height="50" rx="6" fill="none" stroke="#ffffff" stroke-width="1.5" opacity="0.4"/>
                            <!-- Corner highlights -->
                            <circle cx="30" cy="30" r="3" fill="#ffffff" opacity="0.9"/>
                            <circle cx="70" cy="30" r="3" fill="#ffffff" opacity="0.9"/>
                            <circle cx="30" cy="70" r="3" fill="#ffffff" opacity="0.9"/>
                            <circle cx="70" cy="70" r="3" fill="#ffffff" opacity="0.9"/>
                            <!-- 4D Text -->
                            <text x="50" y="58" font-family="Arial, sans-serif" font-size="28" font-weight="900" fill="#ffffff" text-anchor="middle" opacity="1" filter="url(#glow)">4D</text>
                        </g>
                    </svg>
                    <div class="logo-text-container">
                        <div class="logo-main-text">4DLLM Studio by RomanAILabs</div>
                        <div class="logo-email">romanailabs@gmail.com</div>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="main-content">
            <div class="sidebar">
                <div class="sidebar-section">
                    <div class="section-title">Recent Files</div>
                    <div id="recent-files" style="background: rgba(15, 23, 42, 0.5); border-radius: 8px; padding: 12px; min-height: 100px; color: #64748b; font-size: 12px;">
                        No recent files
                    </div>
                </div>
                
                <div class="sidebar-section">
                    <div class="section-title">Files</div>
                    <button class="btn-primary" onclick="window.bridge.open4dllm()">📂 Open .4dllm</button>
                    <button class="btn-secondary" onclick="window.bridge.openGguf()">📂 Open .gguf</button>
                </div>
                
                <div class="sidebar-section">
                    <div class="section-title">Profiles</div>
                    <select id="profile-select" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; margin-bottom: 12px;" onchange="window.bridge.selectProfile(this.value)">
                        <option value="Default">Default</option>
                        <option value="High Performance">High Performance</option>
                        <option value="Low Memory">Low Memory</option>
                    </select>
                    <div id="safety-badge" style="padding: 10px; background: rgba(51, 65, 85, 0.6); border-radius: 8px; text-align: center; font-weight: 600; color: #e2e8f0; margin-bottom: 20px;">SAFE</div>
                </div>
                
                <div class="sidebar-section">
                    <div class="section-title">Navigation</div>
                    <div class="nav-tabs">
                <button class="nav-tab active" onclick="switchTab('run')">▶ Run</button>
                <button class="nav-tab" onclick="switchTab('train')">🎓 Train</button>
                <button class="nav-tab" onclick="switchTab('build')">🔨 Build</button>
                <button class="nav-tab" onclick="switchTab('editor')">✏️ Editor</button>
                <button class="nav-tab" onclick="switchTab('inspect')">🔍 Inspect</button>
                <button class="nav-tab" onclick="switchTab('scripts')">📜 Scripts</button>
                <button class="nav-tab" onclick="switchTab('settings')">⚙️ Settings</button>
                    </div>
                </div>
            </div>
            
            <div class="workspace">
                <div id="tab-run" class="tab-content active">
                    <div class="content-header">Run 4DLLM/GGUF Models</div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">Selected File:</div>
                        <div id="selected-file" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; min-height: 40px; display: flex; align-items: center;">No file selected<br><span style="font-size: 11px; color: #64748b;">(Supports .4dllm and .gguf)</span></div>
                    </div>
                    
                    <div style="display: flex; gap: 12px; margin-bottom: 20px;">
                        <button class="btn-primary" onclick="window.bridge.selectFile()">📂 Select</button>
                        <button class="btn-primary" onclick="window.bridge.runModel()">▶ Run</button>
                        <button class="btn-secondary" onclick="window.bridge.stopModel()">■ Stop</button>
                        <button class="btn-secondary" onclick="window.bridge.copyCommand()">📋 Copy Cmd</button>
                    </div>
                    
                    <div id="safety-warning" style="padding: 16px; background: rgba(251, 191, 36, 0.1); border: 1px solid rgba(251, 191, 36, 0.3); border-radius: 10px; margin-bottom: 16px; display: none;">
                        <div style="color: #fbbf24; font-weight: 600; margin-bottom: 8px;">⚠️ Safety Warning</div>
                        <div id="warning-text" style="color: #fbbf24; font-size: 13px;"></div>
                        <label style="display: flex; align-items: center; gap: 8px; margin-top: 12px; color: #e2e8f0; cursor: pointer;">
                            <input type="checkbox" id="unsafe-check" onchange="window.bridge.toggleUnsafe(this.checked)">
                            <span>Allow Unsafe Modules</span>
                        </label>
                    </div>
                    
                    <div class="terminal-container">
                        <div class="terminal-overlay-logo">
                            <img src="TERMINAL_LOGO_PLACEHOLDER" alt="RomanAILabs Logo" style="width: 288px; height: 288px; object-fit: contain;" onerror="console.error('Failed to load terminal logo:', this.src);">
                        </div>
                        <div class="terminal-output" id="terminal-output"></div>
                        <div class="chat-input-container">
                            <textarea class="chat-input" id="chat-input" placeholder="Type your message here... (Enter to send, Shift+Enter for newline)" onkeydown="handleChatKey(event)"></textarea>
                            <button class="btn-send" onclick="sendChatMessage()">📤 Send</button>
                        </div>
                    </div>
                </div>
                
                <div id="tab-train" class="tab-content">
                    <div class="content-header">🎓 Train 4DLLM/GGUF Model</div>
                    
                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
                        <!-- Left Column: File Selection -->
                        <div style="display: flex; flex-direction: column; gap: 16px;">
                            <div>
                                <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">📂 Input Model File</div>
                                <div id="train-input-file" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; margin-bottom: 12px; min-height: 40px; display: flex; align-items: center;">No file selected<br><span style="font-size: 11px; color: #64748b;">(Supports .4dllm and .gguf)</span></div>
                                <div style="display: flex; gap: 8px; margin-bottom: 16px;">
                                    <button class="btn-primary" onclick="window.bridge.selectTrainInputFile()" style="flex: 1;">📂 Select Model File</button>
                                    <button class="btn-secondary" id="train-load-btn" onclick="window.bridge.loadTrainModel()" style="flex: 1;" disabled>🔍 Load Model</button>
                                </div>
                                <div id="train-model-status" style="padding: 8px; background: rgba(15, 23, 42, 0.4); border-radius: 6px; color: #94a3b8; font-size: 12px; margin-bottom: 8px; display: none;"></div>
                            </div>
                            
                            <div>
                                <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">📊 Training Dataset</div>
                                <div id="train-dataset-file" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; margin-bottom: 12px; min-height: 40px; display: flex; align-items: center;">No dataset selected<br><span style="font-size: 11px; color: #64748b;">(JSON, JSONL, or TXT format)</span></div>
                                <button class="btn-primary" onclick="window.bridge.selectTrainDataset()" style="margin-bottom: 16px;">📊 Select Dataset</button>
                            </div>
                            
                            <div>
                                <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">💾 Output 4DLLM File</div>
                                <div id="train-output-file" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; margin-bottom: 12px; min-height: 40px; display: flex; align-items: center;">Auto-generated from input</div>
                                <button class="btn-secondary" onclick="window.bridge.selectTrainOutputFile()">💾 Choose Output Path</button>
                            </div>
                        </div>
                        
                        <!-- Right Column: Training Configuration -->
                        <div style="display: flex; flex-direction: column; gap: 16px;">
                            <div>
                                <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">⚙️ Training Preset</div>
                                <select id="train-preset" style="width: 100%; padding: 12px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; margin-bottom: 16px;" onchange="window.bridge.loadTrainPreset(this.value)">
                                    <option value="Quick Fine-Tune">Quick Fine-Tune</option>
                                    <option value="Standard Training" selected>Standard Training</option>
                                    <option value="Advanced Training">Advanced Training</option>
                                </select>
                            </div>
                            
                            <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 16px; border: 1px solid rgba(71, 85, 105, 0.3);">
                                <div style="font-size: 14px; color: #06b6d4; margin-bottom: 12px; font-weight: 600;">📋 Training Parameters</div>
                                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">Epochs</label>
                                        <input type="text" id="train-epochs" value="5" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">Learning Rate</label>
                                        <input type="text" id="train-learning-rate" value="0.0001" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">Batch Size</label>
                                        <input type="text" id="train-batch-size" value="8" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">Context Size</label>
                                        <input type="text" id="train-context-size" value="4096" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">LoRA Rank (r)</label>
                                        <input type="text" id="train-lora-r" value="16" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">LoRA Alpha</label>
                                        <input type="text" id="train-lora-alpha" value="32" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div style="grid-column: 1 / -1;">
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px;">LoRA Dropout</label>
                                        <input type="text" id="train-lora-dropout" value="0.1" style="width: 100%; padding: 8px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                </div>
                            </div>
                            
                            <div style="display: flex; gap: 12px; margin-top: 8px;">
                                <button class="btn-primary" onclick="window.bridge.startTraining()" id="train-start-btn" style="flex: 1;">🚀 Start Training</button>
                                <button class="btn-secondary" onclick="window.bridge.stopTraining()" id="train-stop-btn" style="flex: 1; display: none;">■ Stop Training</button>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Training Progress & Stats -->
                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 16px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 12px; font-weight: 600;">📊 Training Statistics</div>
                            <div id="train-stats" style="color: #cbd5e1; font-family: 'Consolas', monospace; font-size: 13px; line-height: 1.8;">
                                <div>Epoch: <span style="color: #06b6d4;">-</span></div>
                                <div>Loss: <span style="color: #06b6d4;">-</span></div>
                                <div>Time: <span style="color: #06b6d4;">-</span></div>
                                <div>Status: <span style="color: #64748b;">Ready</span></div>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 16px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 12px; font-weight: 600;">📈 Progress</div>
                            <div style="background: rgba(0, 0, 0, 0.3); border-radius: 8px; height: 24px; overflow: hidden; margin-bottom: 8px; position: relative;">
                                <div id="train-progress-bar" style="background: linear-gradient(90deg, #06b6d4 0%, #22d3ee 100%); height: 100%; width: 0%; transition: width 0.3s ease; box-shadow: 0 0 10px rgba(6, 182, 212, 0.5);"></div>
                            </div>
                            <div id="train-progress-text" style="color: #94a3b8; font-size: 12px; text-align: center;">Not started</div>
                        </div>
                    </div>
                    
                    <!-- Training Logs -->
                    <div>
                        <div style="font-size: 14px; color: #06b6d4; margin-bottom: 12px; font-weight: 600;">📝 Training Log</div>
                        <div class="terminal-container" style="min-height: 300px;">
                            <div class="terminal-overlay-logo">
                                <img src="TERMINAL_LOGO_PLACEHOLDER" alt="RomanAILabs Logo" style="width: 288px; height: 288px; object-fit: contain;" onerror="console.error('Failed to load terminal logo:', this.src);">
                            </div>
                            <div class="terminal-output" id="train-output"></div>
                        </div>
                    </div>
                </div>
                
                <div id="tab-build" class="tab-content">
                    <div class="content-header">Build 4DLLM Package</div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">GGUF Model:</div>
                        <div id="build-gguf-file" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; margin-bottom: 12px;">No GGUF selected</div>
                        <button class="btn-primary" onclick="window.bridge.selectGguf()">📂 Select GGUF File</button>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">Script Files:</div>
                        <div style="display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap;">
                            <button class="btn-secondary" onclick="window.bridge.loadModules()">📁 Load Scripts</button>
                            <div id="modules-status" style="padding: 8px; color: #94a3b8; font-size: 12px; align-self: center;">No scripts loaded</div>
                        </div>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">Scripts:</div>
                        <div id="scripts-list" style="max-height: 300px; overflow-y: auto; background: rgba(15, 23, 42, 0.6); border-radius: 8px; padding: 12px; margin-bottom: 12px; min-height: 100px;">
                            <div style="color: #64748b; font-size: 12px;">No scripts added. Click 'Load from file' to add scripts.</div>
                        </div>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">System Prompt (Optional):</div>
                        <textarea id="build-system-prompt" placeholder="Enter system prompt for the model (e.g., 'You are a helpful assistant...')" style="width: 100%; min-height: 100px; padding: 12px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; font-family: monospace; font-size: 13px; resize: vertical; margin-bottom: 12px;"></textarea>
                        <div style="display: flex; gap: 8px; margin-bottom: 8px; flex-wrap: wrap;">
                            <button class="btn-secondary" onclick="saveBuilderSystemPrompt()" style="padding: 8px 12px; font-size: 12px; font-weight: 700;">💾 Save Prompt</button>
                            <button class="btn-secondary" onclick="loadBuilderSystemPrompt()" style="padding: 8px 12px; font-size: 12px; font-weight: 700;">🔄 Load Saved</button>
                            <button class="btn-secondary" onclick="clearBuilderSystemPrompt()" style="padding: 8px 12px; font-size: 12px; font-weight: 700;">🧹 Clear</button>
                        </div>
                        <div style="font-size: 11px; color: #64748b; margin-bottom: 12px;">This will be stored in the 4DLLM metadata and can be used by the runner.</div>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <div style="font-size: 14px; color: #94a3b8; margin-bottom: 8px;">Output Path:</div>
                        <div style="display: flex; gap: 8px; margin-bottom: 12px;">
                            <input type="text" id="build-output-path" placeholder="Output .4dllm path (optional)" style="flex: 1; padding: 12px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0;">
                            <button class="btn-secondary" onclick="window.bridge.selectBuildOutputPath()" style="width: auto; padding: 12px 16px; white-space: nowrap;">📂 Browse</button>
                        </div>
                        <div style="display: flex; gap: 12px; margin-bottom: 12px;">
                            <label style="color: #cbd5e1; font-size: 13px; display: flex; align-items: center; gap: 8px; cursor: pointer;">
                                <input type="checkbox" id="compress-scripts" checked style="cursor: pointer;">
                                Compress Scripts
                            </label>
                            <label style="color: #cbd5e1; font-size: 13px; display: flex; align-items: center; gap: 8px; cursor: pointer;">
                                <input type="checkbox" id="compress-metadata" checked style="cursor: pointer;">
                                Compress Metadata
                            </label>
                        </div>
                    </div>
                    <div style="margin-top: 20px;">
                        <button class="btn-primary" onclick="window.bridge.buildPackage()" style="width: 100%;">🔨 Build Package</button>
                    <div id="build-status" style="margin-top: 16px; color: #94a3b8; font-size: 13px;">Ready</div>
                    </div>
                </div>
                
                <div id="tab-editor" class="tab-content">
                    <div class="content-header">Edit GGUF / 4DLLM File</div>
                    <div style="margin-bottom: 20px;">
                        <div style="display: flex; gap: 12px; margin-bottom: 12px;">
                            <button class="btn-primary" onclick="window.bridge.loadEditorFile()">📂 Load File (.gguf or .4dllm)</button>
                            <button class="btn-secondary" onclick="window.bridge.saveEditorFile()">💾 Save Changes</button>
                            <button class="btn-secondary" onclick="window.bridge.createBackup()">📋 Create Backup</button>
                        </div>
                        <div id="editor-file-info" style="padding: 12px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; margin-bottom: 12px;">No file loaded</div>
                    </div>
                    <div style="display: flex; gap: 12px; margin-bottom: 20px;">
                        <button class="btn-secondary" id="easy-mode-btn" onclick="switchEditorMode('easy')">📝 Easy Mode</button>
                        <button class="btn-secondary" id="advanced-mode-btn" onclick="switchEditorMode('advanced')">🔧 Advanced Mode</button>
                    </div>
                    <div id="editor-content" style="flex: 1; overflow-y: auto; min-height: 400px;">
                        <div style="padding: 40px; text-align: center; color: #64748b;">
                            <div style="font-size: 18px; margin-bottom: 12px;">Load a file to start editing</div>
                            <div style="font-size: 13px;">Supports both .gguf and .4dllm files</div>
                        </div>
                    </div>
                </div>
                
                <div id="tab-inspect" class="tab-content">
                    <div class="content-header">Inspect File (GGUF / 4DLLM)</div>
                    <div style="display: flex; gap: 12px; margin-bottom: 20px;">
                        <button class="btn-primary" onclick="window.bridge.inspectFile()">📂 Load File (.gguf or .4dllm)</button>
                        <button class="btn-secondary" onclick="window.bridge.refreshInspect()">🔄 Refresh</button>
                    </div>
                    <div class="terminal-container">
                        <div class="terminal-overlay-logo">
                            <img src="TERMINAL_LOGO_PLACEHOLDER" alt="RomanAILabs Logo" style="width: 288px; height: 288px; object-fit: contain;" onerror="console.error('Failed to load terminal logo:', this.src);">
                        </div>
                        <div class="terminal-output" id="inspect-output"></div>
                    </div>
                </div>
                
                <div id="tab-scripts" class="tab-content">
                    <div class="content-header">Scripts & Modules</div>
                    <div style="display: flex; gap: 12px; margin-bottom: 12px; flex-wrap: wrap;">
                        <button class="btn-primary" onclick="window.bridge.loadScriptsFile()">📂 Load 4DLLM File</button>
                        <button class="btn-secondary" onclick="window.bridge.refreshScripts()">🔄 Refresh</button>
                        <div id="scripts-file-info" style="padding: 10px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; color: #cbd5e1; flex: 1;">No file loaded</div>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <input type="text" id="script-search" placeholder="Search scripts..." oninput="filterScriptsTab()" style="width: 100%; padding: 12px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0;">
                    </div>
                    <div class="terminal-container">
                        <div class="terminal-overlay-logo">
                            <img src="TERMINAL_LOGO_PLACEHOLDER" alt="RomanAILabs Logo" style="width: 288px; height: 288px; object-fit: contain;" onerror="console.error('Failed to load terminal logo:', this.src);">
                        </div>
                        <div class="terminal-output" id="scripts-output">Load a .4dllm file to view scripts</div>
                    </div>
                </div>
                
                <div id="tab-settings" class="tab-content">
                    <div class="content-header">⚙️ Settings</div>
                    
                    <!-- Settings Navigation Tabs -->
                    <div style="display: flex; gap: 8px; margin-bottom: 24px; border-bottom: 2px solid rgba(71, 85, 105, 0.3); padding-bottom: 12px;">
                        <button class="settings-tab-btn active" data-settings-tab="profiles" onclick="switchSettingsTab('profiles')">📋 Profiles</button>
                        <button class="settings-tab-btn" data-settings-tab="general" onclick="switchSettingsTab('general')">⚙️ General</button>
                        <button class="settings-tab-btn" data-settings-tab="performance" onclick="switchSettingsTab('performance')">🚀 Performance</button>
                        <button class="settings-tab-btn" data-settings-tab="ui" onclick="switchSettingsTab('ui')">🎨 Interface</button>
                        <button class="settings-tab-btn" data-settings-tab="advanced" onclick="switchSettingsTab('advanced')">🔧 Advanced</button>
                    </div>
                    
                    <!-- Profiles Tab -->
                    <div id="settings-profiles" class="settings-panel active">
                        <div style="margin-bottom: 24px;">
                            <div style="font-size: 18px; font-weight: 700; color: #06b6d4; margin-bottom: 16px;">📋 Profile Management</div>
                            
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
                                <div>
                                    <label style="display: block; font-size: 13px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">Current Profile</label>
                                    <select id="settings-profile-select" style="width: 100%; padding: 12px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; margin-bottom: 16px;" onchange="window.bridge.selectProfile(this.value)">
                                        <option value="Default">Default</option>
                                        <option value="High Performance">High Performance</option>
                                        <option value="Low Memory">Low Memory</option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display: block; font-size: 13px; color: #94a3b8; margin-bottom: 8px; font-weight: 600;">Actions</label>
                                    <div style="display: flex; gap: 8px;">
                                        <button class="btn-secondary" onclick="createNewProfile()" style="flex: 1;">➕ New Profile</button>
                                        <button class="btn-secondary" onclick="duplicateProfile()" style="flex: 1;">📋 Duplicate</button>
                                        <button class="btn-secondary" onclick="deleteProfile()" style="flex: 1; background: rgba(239, 68, 68, 0.2); border-color: rgba(239, 68, 68, 0.5);">🗑️ Delete</button>
                                    </div>
                                </div>
                            </div>
                            
                            <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                                <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Profile Parameters</div>
                                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Context Size (n_ctx)</label>
                                        <input type="text" id="profile-n-ctx" placeholder="4096" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Threads</label>
                                        <input type="text" id="profile-threads" placeholder="4" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">GPU Layers</label>
                                        <input type="text" id="profile-gpu-layers" placeholder="0" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Max Tokens</label>
                                        <input type="text" id="profile-max-tokens" placeholder="256" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Temperature</label>
                                        <input type="text" id="profile-temp" placeholder="0.7" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                    </div>
                                    <div>
                                        <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Backend</label>
                                        <select id="profile-backend" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                            <option value="llama_cpp">llama_cpp</option>
                                            <option value="llama_cli">llama_cli</option>
                                        </select>
                                    </div>
                                </div>
                                <button class="btn-primary" onclick="saveProfile()" style="margin-top: 16px; width: 100%;">💾 Save Profile Changes</button>
                            </div>
                            
                            <div id="profile-display" style="padding: 16px; background: rgba(15, 23, 42, 0.6); border-radius: 8px; font-family: 'Consolas', monospace; font-size: 12px; white-space: pre-wrap; color: #94a3b8;">Current profile settings will be displayed here when a profile is selected.</div>
                        </div>
                    </div>
                    
                    <!-- General Settings Tab -->
                    <div id="settings-general" class="settings-panel">
                        <div style="font-size: 18px; font-weight: 700; color: #06b6d4; margin-bottom: 24px;">⚙️ General Settings</div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Application Behavior</div>
                            <div style="display: flex; flex-direction: column; gap: 12px;">
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-auto-save" style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Auto-save Settings</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Automatically save settings when changed</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-confirm-exit" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Confirm Before Exit</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Show confirmation dialog when closing the application</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-remember-files" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Remember Recent Files</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Keep track of recently opened files</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-check-updates" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Check for Updates</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Automatically check for application updates on startup</div>
                                    </div>
                                </label>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">File Operations</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Default Save Location</label>
                                    <input type="text" id="setting-default-save-path" placeholder="Leave empty for current directory" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Backup Location</label>
                                    <input type="text" id="setting-backup-path" placeholder="Leave empty for same as file" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Language & Region</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Language</label>
                                    <select id="setting-language" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="en">English</option>
                                        <option value="es">Español</option>
                                        <option value="fr">Français</option>
                                        <option value="de">Deutsch</option>
                                        <option value="ja">日本語</option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Date Format</label>
                                    <select id="setting-date-format" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="YYYY-MM-DD">YYYY-MM-DD</option>
                                        <option value="MM/DD/YYYY">MM/DD/YYYY</option>
                                        <option value="DD/MM/YYYY">DD/MM/YYYY</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Performance Settings Tab -->
                    <div id="settings-performance" class="settings-panel">
                        <div style="font-size: 18px; font-weight: 700; color: #06b6d4; margin-bottom: 24px;">🚀 Performance Settings</div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Memory Management</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Memory Limit (MB)</label>
                                    <input type="text" id="setting-memory-limit" placeholder="0 = Unlimited" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Cache Size (MB)</label>
                                    <input type="text" id="setting-cache-size" placeholder="1024" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Processing</div>
                            <div style="display: flex; flex-direction: column; gap: 12px;">
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-use-gpu" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Use GPU Acceleration</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Enable GPU processing when available</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-multithreading" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Enable Multithreading</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Use multiple CPU cores for processing</div>
                                    </div>
                                </label>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Background Tasks</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Worker Threads</label>
                                    <input type="text" id="setting-worker-threads" placeholder="Auto" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Task Priority</label>
                                    <select id="setting-task-priority" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="low">Low</option>
                                        <option value="normal" selected>Normal</option>
                                        <option value="high">High</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- UI Settings Tab -->
                    <div id="settings-ui" class="settings-panel">
                        <div style="font-size: 18px; font-weight: 700; color: #06b6d4; margin-bottom: 24px;">🎨 Interface Settings</div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Appearance</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Theme</label>
                                    <select id="setting-theme" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="dark" selected>Dark</option>
                                        <option value="light">Light</option>
                                        <option value="auto">Auto (System)</option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Font Size</label>
                                    <select id="setting-font-size" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="small">Small (12px)</option>
                                        <option value="medium" selected>Medium (13px)</option>
                                        <option value="large">Large (14px)</option>
                                        <option value="xlarge">Extra Large (16px)</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Animations & Effects</div>
                            <div style="display: flex; flex-direction: column; gap: 12px;">
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-animations" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Enable Animations</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Smooth transitions and animations</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-effects" checked style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Visual Effects</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Glows, shadows, and other visual enhancements</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-reduced-motion" style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Reduce Motion</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Minimize animations for accessibility</div>
                                    </div>
                                </label>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Terminal Display</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Font Family</label>
                                    <select id="setting-terminal-font" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="Consolas" selected>Consolas</option>
                                        <option value="Monaco">Monaco</option>
                                        <option value="Courier New">Courier New</option>
                                        <option value="Fira Code">Fira Code</option>
                                        <option value="JetBrains Mono">JetBrains Mono</option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Max Log Lines</label>
                                    <input type="text" id="setting-max-log-lines" placeholder="500" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Advanced Settings Tab -->
                    <div id="settings-advanced" class="settings-panel">
                        <div style="font-size: 18px; font-weight: 700; color: #06b6d4; margin-bottom: 24px;">🔧 Advanced Settings</div>
                        
                        <div style="background: rgba(251, 191, 36, 0.1); border: 1px solid rgba(251, 191, 36, 0.3); border-radius: 12px; padding: 16px; margin-bottom: 24px;">
                            <div style="color: #fbbf24; font-weight: 600; margin-bottom: 8px;">⚠️ Warning</div>
                            <div style="color: #fbbf24; font-size: 12px;">Modifying these settings may affect application stability. Only change if you know what you're doing.</div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Logging</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Log Level</label>
                                    <select id="setting-log-level" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                        <option value="DEBUG">DEBUG</option>
                                        <option value="INFO" selected>INFO</option>
                                        <option value="WARNING">WARNING</option>
                                        <option value="ERROR">ERROR</option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Log File Path</label>
                                    <input type="text" id="setting-log-path" placeholder="Default location" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                            </div>
                            <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px; margin-top: 12px;">
                                <input type="checkbox" id="setting-log-to-file" checked style="cursor: pointer;">
                                <div style="font-weight: 600; color: #e2e8f0;">Enable File Logging</div>
                            </label>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Paths & Directories</div>
                            <div style="display: flex; flex-direction: column; gap: 12px;">
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Modules Directory</label>
                                    <input type="text" id="setting-modules-dir" placeholder="modules/" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                                <div>
                                    <label style="display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;">Temp Directory</label>
                                    <input type="text" id="setting-temp-dir" placeholder="System default" style="width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0;">
                                </div>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3); margin-bottom: 20px;">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Developer Options</div>
                            <div style="display: flex; flex-direction: column; gap: 12px;">
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-debug-mode" style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Debug Mode</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Enable verbose debugging output</div>
                                    </div>
                                </label>
                                <label style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; background: rgba(15, 23, 42, 0.3); border-radius: 8px;">
                                    <input type="checkbox" id="setting-dev-tools" style="cursor: pointer;">
                                    <div>
                                        <div style="font-weight: 600; color: #e2e8f0;">Enable Developer Tools</div>
                                        <div style="font-size: 11px; color: #94a3b8;">Access browser developer tools (F12)</div>
                                    </div>
                                </label>
                            </div>
                        </div>
                        
                        <div style="background: rgba(15, 23, 42, 0.4); border-radius: 12px; padding: 20px; border: 1px solid rgba(71, 85, 105, 0.3);">
                            <div style="font-size: 14px; color: #06b6d4; margin-bottom: 16px; font-weight: 600;">Reset & Maintenance</div>
                            <div style="display: flex; gap: 12px;">
                                <button class="btn-secondary" onclick="resetSettings()" style="flex: 1;">🔄 Reset All Settings</button>
                                <button class="btn-secondary" onclick="exportSettings()" style="flex: 1;">📤 Export Settings</button>
                                <button class="btn-secondary" onclick="importSettings()" style="flex: 1;">📥 Import Settings</button>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Settings Action Buttons -->
                    <div style="display: flex; gap: 12px; margin-top: 24px; padding-top: 24px; border-top: 2px solid rgba(71, 85, 105, 0.3);">
                        <button class="btn-primary" onclick="saveAllSettings()" style="flex: 1;">💾 Save All Settings</button>
                        <button class="btn-secondary" onclick="resetToDefaults()" style="flex: 1;">🔄 Reset to Defaults</button>
                    </div>
                </div>
            </div>
            
            <div class="details-panel">
                <div class="details-section">
                    <div class="details-header">Details</div>
                    <div class="details-content" id="details-content">Select a file to view details</div>
                </div>
                <div class="details-section">
                    <div class="details-header">Debug Log</div>
                    <div class="debug-log" id="debug-log">
                        <div class="debug-log-entry info">[System] Debug log initialized</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
    <script>
        // Initialize QWebChannel
        let bridge = null;
        let channelReady = false;
        
        function initBridge() {
            if (typeof qt !== 'undefined' && qt.webChannelTransport) {
                try {
                    new QWebChannel(qt.webChannelTransport, function(channel) {
                        bridge = channel.objects.bridge;
                        window.bridge = bridge;
                        channelReady = true;
                        console.log('QWebChannel bridge connected');
                    });
                } catch (e) {
                    console.error('QWebChannel init error:', e);
                    setupFallbackBridge();
                }
            } else {
                console.warn('QWebChannel transport not available, using fallback');
                setupFallbackBridge();
            }
        }
        
        function setupFallbackBridge() {
            // Fallback bridge for testing (will show errors in console)
            window.bridge = {
                open4dllm: () => console.warn('Bridge not connected: open4dllm'),
                openGguf: () => console.warn('Bridge not connected: openGguf'),
                selectFile: () => console.warn('Bridge not connected: selectFile'),
                runModel: () => console.warn('Bridge not connected: runModel'),
                stopModel: () => console.warn('Bridge not connected: stopModel'),
                copyCommand: () => console.warn('Bridge not connected: copyCommand'),
                selectGguf: () => console.warn('Bridge not connected: selectGguf'),
                buildPackage: () => console.warn('Bridge not connected: buildPackage'),
                inspectFile: () => console.warn('Bridge not connected: inspectFile'),
                refreshInspect: () => console.warn('Bridge not connected: refreshInspect'),
                loadScriptsFile: () => console.warn('Bridge not connected: loadScriptsFile'),
                refreshScripts: () => console.warn('Bridge not connected: refreshScripts'),
                selectProfile: (p) => console.warn('Bridge not connected: selectProfile', p),
                toggleUnsafe: (e) => console.warn('Bridge not connected: toggleUnsafe', e),
                sendChatMessage: (t) => console.warn('Bridge not connected: sendChatMessage', t),
                loadModules: () => console.warn('Bridge not connected: loadModules'),
                loadEditorFile: () => console.warn('Bridge not connected: loadEditorFile'),
                saveEditorFile: () => console.warn('Bridge not connected: saveEditorFile'),
                createBackup: () => console.warn('Bridge not connected: createBackup'),
                refreshEditorDisplay: () => console.warn('Bridge not connected: refreshEditorDisplay'),
                setEditorMode: (m) => console.warn('Bridge not connected: setEditorMode', m),
                selectTrainInputFile: () => console.warn('Bridge not connected: selectTrainInputFile'),
                loadTrainModel: () => console.warn('Bridge not connected: loadTrainModel'),
                selectTrainDataset: () => console.warn('Bridge not connected: selectTrainDataset'),
                selectTrainOutputFile: () => console.warn('Bridge not connected: selectTrainOutputFile'),
                loadTrainPreset: (p) => console.warn('Bridge not connected: loadTrainPreset', p),
                startTraining: () => console.warn('Bridge not connected: startTraining'),
                stopTraining: () => console.warn('Bridge not connected: stopTraining'),
                advancedRefreshMetadata: () => console.warn('Bridge not connected: advancedRefreshMetadata'),
                advancedAddKey: () => console.warn('Bridge not connected: advancedAddKey'),
                advancedDeleteKey: () => console.warn('Bridge not connected: advancedDeleteKey'),
                advancedSaveItemWithData: (k, v, t) => console.warn('Bridge not connected: advancedSaveItemWithData', k, v, t)
            };
        }

        const BUILDER_PROMPT_KEY = 'builder_system_prompt';

        function saveBuilderSystemPrompt() {
            try {
                const el = document.getElementById('build-system-prompt');
                if (!el) return;
                const val = el.value || '';
                // Persist to Python (builder file) when bridge is available
                if (window.bridge && typeof window.bridge.saveBuilderSystemPrompt === 'function') {
                    window.bridge.saveBuilderSystemPrompt(val);
                }
                localStorage.setItem(BUILDER_PROMPT_KEY, val);
                console.log(`[BUILDER PROMPT] Saved (${val.length} chars)`);
            } catch (err) {
                console.warn('Failed to save builder system prompt', err);
            }
        }

        function loadBuilderSystemPrompt() {
            try {
                const el = document.getElementById('build-system-prompt');
                if (!el) return;
                // Ask Python for saved value when bridge is available
                if (window.bridge && typeof window.bridge.loadBuilderSystemPrompt === 'function') {
                    window.bridge.loadBuilderSystemPrompt();
                } else {
                    const val = localStorage.getItem(BUILDER_PROMPT_KEY);
                    if (val !== null) {
                        el.value = val;
                        console.log(`[BUILDER PROMPT] Loaded saved prompt (${val.length} chars)`);
                    }
                }
            } catch (err) {
                console.warn('Failed to load builder system prompt', err);
            }
        }

        function clearBuilderSystemPrompt() {
            try {
                const el = document.getElementById('build-system-prompt');
                if (el) el.value = '';
                if (window.bridge && typeof window.bridge.clearBuilderSystemPrompt === 'function') {
                    window.bridge.clearBuilderSystemPrompt();
                }
                localStorage.removeItem(BUILDER_PROMPT_KEY);
                console.log('[BUILDER PROMPT] Cleared saved prompt');
            } catch (err) {
                console.warn('Failed to clear builder system prompt', err);
            }
        }

        document.addEventListener('DOMContentLoaded', () => {
            loadBuilderSystemPrompt();
        });
        
        // Try to initialize immediately
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initBridge);
        } else {
            initBridge();
        }
        
        // Also try after a short delay in case QWebChannel loads later
        setTimeout(initBridge, 100);
        
        // Tab switching function - direct and simple with comprehensive debugging
        function switchTab(tabName) {
            console.log('[NAV DEBUG] switchTab called with:', tabName);
            console.log('[NAV DEBUG] typeof switchTab:', typeof switchTab);
            try {
                console.log('[NAV DEBUG] Starting tab switch to:', tabName);
                
                // Remove active class from all tabs
                const allTabs = document.querySelectorAll('.nav-tab');
                console.log('[NAV DEBUG] Found', allTabs.length, 'nav tabs');
                for (let i = 0; i < allTabs.length; i++) {
                    allTabs[i].classList.remove('active');
                }
                
                // Remove active class from all tab contents
                const allContents = document.querySelectorAll('.tab-content');
                console.log('[NAV DEBUG] Found', allContents.length, 'tab contents');
                for (let i = 0; i < allContents.length; i++) {
                    allContents[i].classList.remove('active');
                }
                
                // Add active class to selected tab - try multiple selectors
                let selectedTab = null;
                // Try onclick-based selection first (find button with onclick containing this tabName)
                const navButtons = document.querySelectorAll('.nav-tab');
                for (let i = 0; i < navButtons.length; i++) {
                    const btn = navButtons[i];
                    const onclickAttr = btn.getAttribute('onclick');
                    if (onclickAttr && onclickAttr.includes("'" + tabName + "'")) {
                        selectedTab = btn;
                        console.log('[NAV DEBUG] Found tab button by onclick attribute');
                        break;
                    }
                }
                
                if (selectedTab) {
                    selectedTab.classList.add('active');
                    console.log('[NAV DEBUG] Added active class to tab button');
                } else {
                    console.warn('[NAV DEBUG] Could not find tab button for:', tabName);
                }
                
                // Show corresponding content
                const targetContent = document.getElementById('tab-' + tabName);
                if (targetContent) {
                    targetContent.classList.add('active');
                    console.log('[NAV DEBUG] Added active class to tab content:', 'tab-' + tabName);
                } else {
                    console.error('[NAV DEBUG] Could not find tab content element: tab-' + tabName);
                }
                
                console.log('[NAV DEBUG] Tab switch completed successfully');
            } catch (err) {
                console.error('[NAV DEBUG] Error switching tab:', err);
                console.error('[NAV DEBUG] Error stack:', err.stack);
            }
        }
        
        // Make switchTab globally available
        window.switchTab = switchTab;
        console.log('[NAV DEBUG] switchTab function defined and assigned to window.switchTab');
        
        // Tab switching uses inline onclick handlers
        console.log('[NAV DEBUG] Script section loaded');
        console.log('[NAV DEBUG] typeof switchTab:', typeof switchTab);
        console.log('[NAV DEBUG] typeof window.switchTab:', typeof window.switchTab);
        
        // Test after a delay to ensure everything is loaded
        setTimeout(function() {
            console.log('[NAV DEBUG] === After 500ms timeout ===');
            console.log('[NAV DEBUG] typeof switchTab:', typeof switchTab);
            console.log('[NAV DEBUG] typeof window.switchTab:', typeof window.switchTab);
            const testButtons = document.querySelectorAll('.nav-tab');
            console.log('[NAV DEBUG] Found', testButtons.length, 'nav buttons');
            testButtons.forEach(function(btn, idx) {
                console.log('[NAV DEBUG] Button', idx, ':', {
                    text: btn.textContent.trim(),
                    onclick: btn.getAttribute('onclick'),
                    hasActive: btn.classList.contains('active')
                });
            });
        }, 500);
        
        // Chat input handling
        function handleChatKey(event) {
            if (event.key === 'Enter' && !event.shiftKey) {
                event.preventDefault();
                sendChatMessage();
            }
        }
        
        function sendChatMessage() {
            const input = document.getElementById('chat-input');
            const text = input.value.trim();
            if (text && window.bridge) {
                window.bridge.sendChatMessage(text);
                input.value = '';
            }
        }
        
        // Editor mode switching
        function switchEditorMode(mode) {
            window.editorMode = mode;
            // Store mode and refresh the editor display
            // The Python side will check window.editorMode via execute_js
            // Update button states
            const easyBtn = document.getElementById('easy-mode-btn');
            const advancedBtn = document.getElementById('advanced-mode-btn');
            if (easyBtn && advancedBtn) {
                if (mode === 'easy') {
                    easyBtn.classList.add('active');
                    advancedBtn.classList.remove('active');
                } else {
                    easyBtn.classList.remove('active');
                    advancedBtn.classList.add('active');
                }
            }
            // Refresh the editor display
            if (window.bridge && window.bridge.setEditorMode) {
                window.bridge.setEditorMode(mode);
            }
            if (window.bridge && window.bridge.refreshEditorDisplay) {
                window.bridge.refreshEditorDisplay();
            }
        }
        
        // Initialize editor mode
        window.editorMode = 'easy';
        
        // Settings tab switching
        function switchSettingsTab(tabName) {
            // Hide all panels
            document.querySelectorAll('.settings-panel').forEach(panel => {
                panel.classList.remove('active');
            });
            // Remove active from all buttons
            document.querySelectorAll('.settings-tab-btn').forEach(btn => {
                btn.classList.remove('active');
            });
            // Show selected panel
            const panel = document.getElementById(`settings-${tabName}`);
            if (panel) {
                panel.classList.add('active');
            }
            // Activate selected button
            const btn = document.querySelector(`.settings-tab-btn[data-settings-tab="${tabName}"]`);
            if (btn) {
                btn.classList.add('active');
            }
        }
        
        // Settings functions (placeholder implementations)
        function createNewProfile() {
            const name = prompt('Enter profile name:');
            if (name && name.trim()) {
                console.log('Creating new profile:', name);
                // Implementation would go here
            }
        }
        
        function duplicateProfile() {
            console.log('Duplicating current profile');
            // Implementation would go here
        }
        
        function deleteProfile() {
            if (confirm('Are you sure you want to delete this profile?')) {
                console.log('Deleting profile');
                // Implementation would go here
            }
        }
        
        function saveProfile() {
            console.log('Saving profile changes');
            // Implementation would go here
        }
        
        function saveAllSettings() {
            console.log('Saving all settings');
            alert('Settings saved! (This is a placeholder - actual implementation would save to storage)');
        }
        
        function resetToDefaults() {
            if (confirm('Are you sure you want to reset all settings to defaults?')) {
                console.log('Resetting to defaults');
                // Implementation would go here
            }
        }
        
        function resetSettings() {
            if (confirm('Are you sure you want to reset all settings? This cannot be undone.')) {
                console.log('Resetting settings');
                // Implementation would go here
            }
        }
        
        function exportSettings() {
            console.log('Exporting settings');
            alert('Settings export feature - would export to JSON file');
        }

        function filterScriptsTab() {
            const query = (document.getElementById('script-search')?.value || '').toLowerCase();
            const cards = document.querySelectorAll('.script-card');
            if (!cards || cards.length === 0) return;
            cards.forEach(card => {
                const hay = (card.getAttribute('data-search') || '').toLowerCase();
                card.style.display = hay.includes(query) ? 'block' : 'none';
            });
        }
        
        function importSettings() {
            console.log('Importing settings');
            alert('Settings import feature - would import from JSON file');
        }
        
        // Builder scripts management
        if (!window.builderScripts) {
            window.builderScripts = [];
        }
        
        function updateScriptsList() {
            const scriptsList = document.getElementById('scripts-list');
            if (!scriptsList) return;
            
            // Update status count
            const totalCount = window.builderScripts ? window.builderScripts.length : 0;
            const enabledCount = window.builderScripts ? window.builderScripts.filter(s => s.enabled !== false).length : 0;
            const statusEl = document.getElementById('modules-status');
            if (statusEl) {
                if (totalCount === 0) {
                    statusEl.textContent = 'No scripts loaded';
                } else {
                    statusEl.textContent = `Loaded ${totalCount} script(s) (${enabledCount} enabled)`;
                }
            }
            
            if (!window.builderScripts || window.builderScripts.length === 0) {
                scriptsList.innerHTML = '<div style="color: #64748b; font-size: 12px;">No scripts added. Click "Load from file" to add scripts.</div>';
                return;
            }
            
            // Sort scripts by priority (lower priority number = higher priority)
            const sortedScripts = [...window.builderScripts].sort((a, b) => (a.priority || 0) - (b.priority || 0));
            
            let html = '<div style="display: flex; flex-direction: column; gap: 8px;">';
            sortedScripts.forEach((script, index) => {
                const size = new Blob([script.content || '']).size;
                const sizeKB = (size / 1024).toFixed(2);
                const enabled = script.enabled !== false; // Default to true
                // Find original index in window.builderScripts array (not sorted)
                const originalIndex = window.builderScripts.findIndex(s => s.name === script.name);
                const scriptIndex = originalIndex >= 0 ? originalIndex : index;
                const scriptNameDisplay = (script.name || 'Unnamed').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                html += `
                    <div style="background: rgba(30, 41, 59, 0.6); border: 1px solid ${enabled ? 'rgba(71, 85, 105, 0.4)' : 'rgba(100, 116, 139, 0.3)'}; border-radius: 8px; padding: 12px; opacity: ${enabled ? '1' : '0.7'};" data-script-index="${scriptIndex}">
                        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                            <div style="display: flex; align-items: center; gap: 8px; flex: 1;">
                                <span style="font-weight: 600; color: ${enabled ? '#e2e8f0' : '#64748b'};">
                                    ${enabled ? '🐍' : '⭕'} ${scriptNameDisplay}
                                </span>
                                <span style="font-size: 11px; color: #94a3b8;">(${sizeKB} KB)</span>
                                <span style="font-size: 10px; color: #64748b; background: rgba(71, 85, 105, 0.3); padding: 2px 6px; border-radius: 4px;">Priority: ${script.priority || 0}</span>
                                ${script.imports && script.imports.length > 0 ? `<span style="font-size: 10px; color: #06b6d4; background: rgba(6, 182, 212, 0.2); padding: 2px 6px; border-radius: 4px;">📦 ${script.imports.length} import${script.imports.length !== 1 ? 's' : ''}</span>` : ''}
                                ${enabled ? '<span style="font-size: 10px; color: #34d399; background: rgba(52, 211, 153, 0.2); padding: 2px 6px; border-radius: 4px;">Enabled</span>' : '<span style="font-size: 10px; color: #fbbf24; background: rgba(251, 191, 36, 0.2); padding: 2px 6px; border-radius: 4px;">Disabled</span>'}
                            </div>
                            <div style="display: flex; gap: 4px;">
                                <button class="toggle-script-btn" data-script-index="${scriptIndex}" 
                                        style="background: ${enabled ? 'rgba(34, 197, 94, 0.2)' : 'rgba(100, 116, 139, 0.2)'}; 
                                               border: 1px solid ${enabled ? 'rgba(34, 197, 94, 0.5)' : 'rgba(100, 116, 139, 0.5)'}; 
                                               color: ${enabled ? '#4ade80' : '#94a3b8'}; padding: 4px 10px; border-radius: 6px; cursor: pointer; 
                                               font-size: 11px; font-weight: 600;">${enabled ? '✓' : '○'}</button>
                                <button class="remove-script-btn" data-script-index="${scriptIndex}" 
                                        style="background: rgba(239, 68, 68, 0.2); border: 1px solid rgba(239, 68, 68, 0.5); 
                                               color: #f87171; padding: 4px 12px; border-radius: 6px; cursor: pointer; 
                                               font-size: 11px; font-weight: 600;">Remove</button>
                            </div>
                        </div>
                        ${script.description ? `<div style="font-size: 11px; color: #94a3b8; margin-bottom: 4px;">${(script.description || '').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>` : ''}
                        <div style="font-size: 10px; color: #64748b;">Source: ${(script.source_path || 'Manual entry').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
                    </div>
                `;
            });
            html += '</div>';
            scriptsList.innerHTML = html;
            
            // Attach event listeners using event delegation to avoid inline onclick issues
            scriptsList.querySelectorAll('.toggle-script-btn').forEach(function(btn) {
                btn.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const scriptIndex = parseInt(this.getAttribute('data-script-index'), 10);
                    if (!isNaN(scriptIndex) && window.builderScripts && window.builderScripts[scriptIndex]) {
                        const script = window.builderScripts[scriptIndex];
                        script.enabled = !script.enabled;
                        updateScriptsList();
                    }
                });
            });
            
            scriptsList.querySelectorAll('.remove-script-btn').forEach(function(btn) {
                btn.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const scriptIndex = parseInt(this.getAttribute('data-script-index'), 10);
                    if (!isNaN(scriptIndex) && window.builderScripts && window.builderScripts[scriptIndex] && window.bridge && window.bridge.removeScript) {
                        const script = window.builderScripts[scriptIndex];
                        window.bridge.removeScript(script.name);
                    }
                });
            });
        }
        
        // Toggle script enabled/disabled
        function toggleScriptEnabled(scriptName) {
            if (!window.builderScripts) return;
            const script = window.builderScripts.find(s => s.name === scriptName);
            if (script) {
                script.enabled = !script.enabled;
                updateScriptsList();
            }
        }
        
        // Initialize scripts list on page load
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', updateScriptsList);
        } else {
            updateScriptsList();
        }
    </script>
</body>
</html>
"""

# Ensure Qt is available before defining Qt-dependent classes
if not HAS_PYQT6:
    raise ImportError("PyQt6 or PySide6 is required but not installed. Please install with: pip install PyQt6")


class WebBridge(QWidget):
    """Bridge object for Python-Qt communication"""
    # Signals
    open4dllmRequested = pyqtSignal()
    openGgufRequested = pyqtSignal()
    selectFileRequested = pyqtSignal()
    runModelRequested = pyqtSignal()
    stopModelRequested = pyqtSignal()
    copyCommandRequested = pyqtSignal()
    selectGgufRequested = pyqtSignal()
    buildPackageRequested = pyqtSignal()
    inspectFileRequested = pyqtSignal()
    refreshInspectRequested = pyqtSignal()
    loadScriptsFileRequested = pyqtSignal()
    refreshScriptsRequested = pyqtSignal()
    selectProfileRequested = pyqtSignal(str)
    toggleUnsafeRequested = pyqtSignal(bool)
    sendChatMessageRequested = pyqtSignal(str)
    # Builder signals
    loadModulesRequested = pyqtSignal()
    addScriptRequested = pyqtSignal(str, str, int, bool)  # name, content, priority, enabled
    removeScriptRequested = pyqtSignal(str)
    selectBuildOutputPathRequested = pyqtSignal()
    # Editor signals
    loadEditorFileRequested = pyqtSignal()
    saveEditorFileRequested = pyqtSignal()
    createBackupRequested = pyqtSignal()
    refreshEditorDisplayRequested = pyqtSignal()
    setEditorModeRequested = pyqtSignal(str)
    getEditorMetadataRequested = pyqtSignal()
    updateEditorMetadataRequested = pyqtSignal(str, str)  # key, value
    # Advanced editor signals
    advancedRefreshMetadataRequested = pyqtSignal()
    advancedAddKeyRequested = pyqtSignal()
    advancedDeleteKeyRequested = pyqtSignal()
    advancedSaveItemRequested = pyqtSignal(str, str, str)  # key, value, value_type
    # Builder system prompt persistence
    saveBuilderSystemPromptRequested = pyqtSignal(str)
    loadBuilderSystemPromptRequested = pyqtSignal()
    clearBuilderSystemPromptRequested = pyqtSignal()
    # Trainer signals
    selectTrainInputFileRequested = pyqtSignal()
    selectTrainDatasetRequested = pyqtSignal()
    selectTrainOutputFileRequested = pyqtSignal()
    loadTrainModelRequested = pyqtSignal()
    loadTrainPresetRequested = pyqtSignal(str)
    startTrainingRequested = pyqtSignal()
    stopTrainingRequested = pyqtSignal()
    
    def __init__(self):
        super().__init__()
    
    @pyqtSlot()
    def open4dllm(self):
        self.open4dllmRequested.emit()
    
    @pyqtSlot()
    def openGguf(self):
        self.openGgufRequested.emit()
    
    @pyqtSlot()
    def selectFile(self):
        self.selectFileRequested.emit()
    
    @pyqtSlot()
    def runModel(self):
        self.runModelRequested.emit()
    
    @pyqtSlot()
    def stopModel(self):
        self.stopModelRequested.emit()
    
    @pyqtSlot()
    def copyCommand(self):
        self.copyCommandRequested.emit()
    
    @pyqtSlot()
    def selectGguf(self):
        self.selectGgufRequested.emit()
    
    @pyqtSlot()
    def buildPackage(self):
        self.buildPackageRequested.emit()
    
    @pyqtSlot()
    def selectBuildOutputPath(self):
        self.selectBuildOutputPathRequested.emit()
    
    @pyqtSlot()
    def inspectFile(self):
        self.inspectFileRequested.emit()
    
    @pyqtSlot()
    def refreshInspect(self):
        self.refreshInspectRequested.emit()

    @pyqtSlot()
    def loadScriptsFile(self):
        self.loadScriptsFileRequested.emit()

    @pyqtSlot()
    def refreshScripts(self):
        self.refreshScriptsRequested.emit()
    
    @pyqtSlot(str)
    def selectProfile(self, profile: str):
        self.selectProfileRequested.emit(profile)
    
    @pyqtSlot(bool)
    def toggleUnsafe(self, enabled: bool):
        self.toggleUnsafeRequested.emit(enabled)
    
    @pyqtSlot(str)
    def sendChatMessage(self, text: str):
        self.sendChatMessageRequested.emit(text)
    
    # Builder slots
    @pyqtSlot()
    def loadModules(self):
        self.loadModulesRequested.emit()
    
    @pyqtSlot(str, str, int, bool)
    def addScript(self, name: str, content: str, priority: int, enabled: bool):
        self.addScriptRequested.emit(name, content, priority, enabled)
    
    @pyqtSlot(str)
    def removeScript(self, name: str):
        self.removeScriptRequested.emit(name)
    
    # Editor slots
    @pyqtSlot()
    def loadEditorFile(self):
        self.loadEditorFileRequested.emit()
    
    @pyqtSlot()
    def saveEditorFile(self):
        self.saveEditorFileRequested.emit()
    
    @pyqtSlot()
    def createBackup(self):
        self.createBackupRequested.emit()

    @pyqtSlot(str)
    def setEditorMode(self, mode: str):
        self.setEditorModeRequested.emit(mode)
    
    @pyqtSlot()
    def refreshEditorDisplay(self):
        self.refreshEditorDisplayRequested.emit()
    
    @pyqtSlot()
    def getEditorMetadata(self):
        self.getEditorMetadataRequested.emit()
    
    @pyqtSlot(str, str)
    def updateEditorMetadata(self, key: str, value: str):
        self.updateEditorMetadataRequested.emit(key, value)
    
    # Advanced editor slots
    @pyqtSlot()
    def advancedRefreshMetadata(self):
        self.advancedRefreshMetadataRequested.emit()
    
    @pyqtSlot()
    def advancedAddKey(self):
        self.advancedAddKeyRequested.emit()
    
    @pyqtSlot()
    def advancedDeleteKey(self):
        self.advancedDeleteKeyRequested.emit()
    
    @pyqtSlot()
    def advancedSaveItem(self):
        # Get values from JavaScript and emit
        # We'll use JavaScript to call this with the actual values
        # For now, we need a way to pass the data - using a workaround with executeJS
        pass
    
    @pyqtSlot(str, str, str)
    def advancedSaveItemWithData(self, key: str, value: str, value_type: str):
        self.advancedSaveItemRequested.emit(key, value, value_type)
    
    # Trainer slots
    @pyqtSlot()
    def selectTrainInputFile(self):
        self.selectTrainInputFileRequested.emit()
    
    @pyqtSlot()
    def loadTrainModel(self):
        self.loadTrainModelRequested.emit()
    
    @pyqtSlot()
    def selectTrainDataset(self):
        self.selectTrainDatasetRequested.emit()
    
    @pyqtSlot()
    def selectTrainOutputFile(self):
        self.selectTrainOutputFileRequested.emit()
    
    @pyqtSlot(str)
    def loadTrainPreset(self, preset: str):
        self.loadTrainPresetRequested.emit(preset)
    
    @pyqtSlot()
    def startTraining(self):
        try:
            DEBUG_LOGGER.info("="*80)
            DEBUG_LOGGER.info("WebBridge.startTraining() CALLED FROM JAVASCRIPT")
            DEBUG_LOGGER.info("="*80)
            self.startTrainingRequested.emit()
            DEBUG_LOGGER.info("startTrainingRequested signal emitted successfully")
        except Exception as e:
            DEBUG_LOGGER.error(f"CRITICAL ERROR in WebBridge.startTraining: {e}", exc_info=True)
            import traceback
            DEBUG_LOGGER.error(f"Traceback:\n{traceback.format_exc()}")
            raise
    
    @pyqtSlot()
    def stopTraining(self):
        self.stopTrainingRequested.emit()

    @pyqtSlot(str)
    def saveBuilderSystemPrompt(self, text):
        self.saveBuilderSystemPromptRequested.emit(text)

    @pyqtSlot()
    def loadBuilderSystemPrompt(self):
        self.loadBuilderSystemPromptRequested.emit()

    @pyqtSlot()
    def clearBuilderSystemPrompt(self):
        self.clearBuilderSystemPromptRequested.emit()


class OutputReaderThread(QThread):
    """Thread for reading subprocess output - optimized for speed"""
    outputReceived = pyqtSignal(str)
    finished = pyqtSignal()
    
    def __init__(self, process):
        super().__init__()
        self.process = process
        self.running = True
    
    def run(self):
        """Read output with LIGHTNING FAST polling - optimized for real-time"""
        buffer_size = 1024  # Smaller chunks for smoother live streaming
        
        while self.running:
            try:
                # Check if process ended
                if self.process.poll() is not None:
                    # Process finished, read any remaining data
                    try:
                        remaining = self.process.stdout.read()
                        if remaining:
                            self.outputReceived.emit(remaining)
                    except:
                        pass
                    break
                
                # LIGHTNING FAST non-blocking read check (1ms timeout)
                if sys.platform != "win32":
                    try:
                        ready, _, _ = select.select([self.process.stdout], [], [], 0.001)
                        if not ready:
                            time.sleep(0.001)  # Minimal sleep - 1ms
                            continue
                    except (ValueError, OSError):
                        break
                
                # Read chunk immediately
                try:
                    chunk = self.process.stdout.read(buffer_size)
                except (ValueError, OSError, AttributeError):
                    break
                
                if not chunk:
                    # No data, minimal sleep
                    time.sleep(0.001)  # 1ms
                    continue
                
                # Emit immediately for live streaming (no line buffering)
                self.outputReceived.emit(chunk)
                
            except Exception as e:
                DEBUG_LOGGER.error(f"Reader error: {e}", exc_info=True)
                break
        
        self.finished.emit()
    
    def stop(self):
        """Stop the reader thread"""
        self.running = False


class WorldClassStudio(QMainWindow):
    """World-class 4DLLM Studio with PyQt6 + HTML/CSS/JS"""
    
    # Signals for thread-safe UI updates
    trainLogAppended = pyqtSignal(str, str)  # text, tag
    trainStatsUpdated = pyqtSignal(str)  # stats HTML
    trainProgressUpdated = pyqtSignal(int, str)  # progress percent, text
    terminalAppendRequested = pyqtSignal(str, str)  # text, tag
    showErrorDialogRequested = pyqtSignal(str, str, str)  # title, message, details
    displayEditorMetadataRequested = pyqtSignal(object, str)  # metadata(dict), file_path(str)
    storeEditorInstanceRequested = pyqtSignal(object, object, object, object, str)  # editor, file_path, temp_gguf, metadata, file_type
    
    def __init__(self):
        super().__init__()
        self.setWindowTitle("4DLLM Studio — RomanAILabs")
        self.setGeometry(100, 100, 1600, 1000)
        
        # Connect training signals
        self.trainLogAppended.connect(self._append_train_log_safe)
        self.trainStatsUpdated.connect(self._update_train_stats_safe)
        self.trainProgressUpdated.connect(self._update_train_progress_safe)
        
        # Connect editor signals for thread-safe UI updates
        self.terminalAppendRequested.connect(self._append_terminal)
        self.showErrorDialogRequested.connect(self._show_error_dialog)
        self.displayEditorMetadataRequested.connect(self._display_editor_metadata_safe)
        self.storeEditorInstanceRequested.connect(self._store_editor_instance_safe)
        
        # No longer using signal-based approach - using queue instead
        
        # JavaScript execution queue for thread-safe UI updates
        self.js_queue = queue.Queue()
        self.js_timer = QTimer()
        self.js_timer.timeout.connect(self._process_js_queue)
        self.js_timer.start(10)  # Check queue every 10ms
        
        # State
        self.run_path: Optional[Path] = None
        self.gguf_path: Optional[Path] = None
        self.is_gguf_file: bool = False  # Track if run_path is a GGUF file
        # Modules folder (same as standalone builder)
        self.script_dir = Path(__file__).resolve().parent
        self.modules_dir = self.script_dir / MODULES_DIRNAME
        
        # Store scripts in Python (like standalone builder) - avoids JavaScript async issues
        # This is the source of truth, JavaScript is just for UI display
        self.builder_scripts: List[Dict[str, Any]] = []
        
        self.proc: Optional[subprocess.Popen] = None
        self.reader_thread: Optional[OutputReaderThread] = None
        self.unsafe_enabled = True
        self.waiting_for_ai = False  # Track if AI is generating
        self.streaming_indicator = None  # Timer for streaming dots
        self.last_ai_line_time = 0  # Track when we last saw an "AI>" line
        self.last_output_time = 0  # Track when we last saw ANY output
        self.ai_timeout_id = None  # Timeout to reset waiting state if detection fails
        self.inactivity_timeout_id = None  # Shorter timeout for inactivity detection
        self._last_log_times = {}  # Track last time each log message was printed (for deduplication)
        self._last_debug_log_messages = {}  # Track last debug log messages to prevent duplicates (message -> timestamp)
        
        # Editor state
        self._editor_instance = None
        self._editor_file_path = None
        
        # Trainer state
        self.train_input_file: Optional[Path] = None
        self.train_output_file: Optional[Path] = None
        self.train_dataset_file: Optional[Path] = None
        self.training_process: Optional[subprocess.Popen] = None
        self.training_thread: Optional[threading.Thread] = None
        self.is_training = False
        
        self.profiles = {
            "Default": {"n_ctx": "4096", "threads": "4", "gpu_layers": "0", "max_tokens": "256", "temp": "0.7", "backend": "llama_cpp"},
            "High Performance": {"n_ctx": "8192", "threads": "8", "gpu_layers": "35", "max_tokens": "512", "temp": "0.8", "backend": "llama_cpp"},
            "Low Memory": {"n_ctx": "2048", "threads": "2", "gpu_layers": "0", "max_tokens": "128", "temp": "0.6", "backend": "llama_cli"},
        }
        self.current_profile = self.profiles["Default"].copy()
        
        # Setup UI
        self._setup_ui()
        self._setup_bridge()
        # Setup GUI debug log handler (after bridge is set up)
        QTimer.singleShot(500, self._setup_debug_log_handler)
        # Setup GUI debug log handler (after bridge is set up)
        QTimer.singleShot(500, self._setup_debug_log_handler)
    
    def _setup_ui(self):
        """Setup the main UI with QWebEngineView"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # Get assets path for logo
        assets_dir = Path(__file__).parent / "assets"
        logo_path = assets_dir / "RomanAILabs.png"
        header_logo_path = assets_dir / "RomanAILabs.png"
        
        # Replace logo placeholders in HTML template
        html_template = HTML_TEMPLATE
        
        # Convert paths to file:// URLs for QWebEngineView
        if logo_path.exists():
            logo_url = QUrl.fromLocalFile(str(logo_path.absolute())).toString()
            DEBUG_LOGGER.info(f"Terminal logo URL: {logo_url}")
            # Replace all terminal logo placeholders
            html_template = html_template.replace('TERMINAL_LOGO_PLACEHOLDER', logo_url)
            html_template = html_template.replace('TERMINAL_LOGO_DIM_PLACEHOLDER', logo_url)
            html_template = html_template.replace('TERMINAL_LOGO_BOLD_PLACEHOLDER', logo_url)
        else:
            DEBUG_LOGGER.warning(f"Terminal logo not found at: {logo_path}")
            html_template = html_template.replace('TERMINAL_LOGO_PLACEHOLDER', '')
            html_template = html_template.replace('TERMINAL_LOGO_DIM_PLACEHOLDER', '')
            html_template = html_template.replace('TERMINAL_LOGO_BOLD_PLACEHOLDER', '')
        
        if header_logo_path.exists():
            header_logo_url = QUrl.fromLocalFile(str(header_logo_path.absolute())).toString()
            DEBUG_LOGGER.info(f"Header logo URL: {header_logo_url}")
            html_template = html_template.replace('HEADER_LOGO_PLACEHOLDER', header_logo_url)
        else:
            DEBUG_LOGGER.warning(f"Header logo not found at: {header_logo_path}")
            html_template = html_template.replace('HEADER_LOGO_PLACEHOLDER', '')
        
        # Web view for HTML/CSS/JS interface
        self.web_view = QWebEngineView()
        # Set base URL to allow loading local files
        base_url = QUrl.fromLocalFile(str(Path(__file__).parent.absolute()) + "/")
        self.web_view.setHtml(html_template, base_url)
        layout.addWidget(self.web_view)
    
    def _setup_bridge(self):
        """Setup WebChannel bridge for Python-JS communication"""
        self.bridge = WebBridge()
        self.channel = QWebChannel()
        self.channel.registerObject("bridge", self.bridge)
        self.web_view.page().setWebChannel(self.channel)
        
        # Connect signals
        self.bridge.open4dllmRequested.connect(self._open_4dllm)
        self.bridge.openGgufRequested.connect(self._open_gguf)
        self.bridge.selectFileRequested.connect(self._select_file)
        self.bridge.runModelRequested.connect(self._run_model)
        self.bridge.stopModelRequested.connect(self._stop_model)
        self.bridge.copyCommandRequested.connect(self._copy_command)
        self.bridge.selectGgufRequested.connect(self._select_gguf)
        self.bridge.buildPackageRequested.connect(self._build_package)
        self.bridge.inspectFileRequested.connect(self._inspect_file)
        self.bridge.refreshInspectRequested.connect(self._refresh_inspect)
        self.bridge.loadScriptsFileRequested.connect(self._load_scripts_file)
        self.bridge.refreshScriptsRequested.connect(self._refresh_scripts)
        self.bridge.selectProfileRequested.connect(self._select_profile)
        self.bridge.toggleUnsafeRequested.connect(self._toggle_unsafe)
        self.bridge.sendChatMessageRequested.connect(self._send_chat_message)
        # Builder signals
        self.bridge.loadModulesRequested.connect(self._load_modules)
        self.bridge.addScriptRequested.connect(self._add_script)
        self.bridge.removeScriptRequested.connect(self._remove_script)
        self.bridge.selectBuildOutputPathRequested.connect(self._select_build_output_path)
        # Builder system prompt persistence
        self.builder_prompt_file = self.script_dir / "builder_system_prompt.json"
        self.bridge.saveBuilderSystemPromptRequested.connect(self._save_builder_system_prompt)
        self.bridge.loadBuilderSystemPromptRequested.connect(self._load_builder_system_prompt)
        self.bridge.clearBuilderSystemPromptRequested.connect(self._clear_builder_system_prompt)
        
        # Auto-load modules folder on startup (like standalone builder)
        QTimer.singleShot(500, self._auto_load_modules_folder)
        # Editor signals
        self.bridge.loadEditorFileRequested.connect(self._load_editor_file)
        self.bridge.saveEditorFileRequested.connect(self._save_editor_file)
        self.bridge.createBackupRequested.connect(self._create_backup)
        self.bridge.refreshEditorDisplayRequested.connect(self._refresh_editor_display)
        self.bridge.setEditorModeRequested.connect(self._set_editor_mode)
        self.bridge.getEditorMetadataRequested.connect(self._get_editor_metadata)
        self.bridge.updateEditorMetadataRequested.connect(self._update_editor_metadata)
        # Advanced editor signals
        self.bridge.advancedRefreshMetadataRequested.connect(self._advanced_refresh_metadata)
        self.bridge.advancedAddKeyRequested.connect(self._advanced_add_key)
        self.bridge.advancedDeleteKeyRequested.connect(self._advanced_delete_key)
        self.bridge.advancedSaveItemRequested.connect(self._advanced_save_item)
        # Initialize editor mode
        self._editor_mode = 'easy'
        # Trainer signals
        self.bridge.selectTrainInputFileRequested.connect(self._select_train_input_file)
        self.bridge.selectTrainDatasetRequested.connect(self._select_train_dataset)
        self.bridge.selectTrainOutputFileRequested.connect(self._select_train_output_file)
        self.bridge.loadTrainModelRequested.connect(self._load_train_model)
        self.bridge.loadTrainPresetRequested.connect(self._load_train_preset)
        self.bridge.startTrainingRequested.connect(self._start_training)
        self.bridge.stopTrainingRequested.connect(self._stop_training)
        DEBUG_LOGGER.info("Training signals connected: startTrainingRequested -> _start_training")
    
    def _generate_error_code(self, error_type: str = "INSPECT") -> str:
        """Generate a unique error code for tracking"""
        import time
        import random
        timestamp = int(time.time() * 1000)
        random_part = random.randint(1000, 9999)
        return f"{error_type}-{timestamp}-{random_part}"
    
    def _show_inspect_error_dialog(self, error: Exception, context: str = ""):
        """Show error dialog for inspect feature - SELF-HEALING: Never crashes"""
        # OUTER TRY: Catch everything
        try:
            import traceback
            # Safe error code generation
            try:
                error_code = self._generate_error_code("INSPECT")
            except Exception:
                error_code = "INSPECT-UNKNOWN"
            
            # Safe error type/message extraction
            try:
                error_type = type(error).__name__ if error else "UnknownError"
                error_msg = str(error) if error else "Unknown error"
            except Exception:
                error_type = "UnknownError"
                error_msg = "Failed to extract error message"
            
            # Safe traceback generation
            try:
                traceback_str = traceback.format_exc()
            except Exception:
                traceback_str = "Failed to generate traceback"
            
            # Safe message construction
            try:
                full_message = f"An error occurred while inspecting the file.\n\n"
                if context:
                    full_message += f"Context: {context}\n\n"
                full_message += f"Error Type: {error_type}\nError Message: {error_msg}"
            except Exception:
                full_message = "An error occurred while inspecting the file."
            
            # Safe error details construction
            try:
                error_details = f"Error Code: {error_code}\n\n"
                error_details += f"Error Type: {error_type}\n"
                error_details += f"Error Message: {error_msg}\n\n"
                error_details += f"Traceback:\n{traceback_str}"
            except Exception:
                error_details = f"Error Code: {error_code}\n\nError: {error_msg}"
            
            # Log the error - wrapped in try-except
            try:
                DEBUG_LOGGER.error(f"[INSPECT] Error Code: {error_code}")
                DEBUG_LOGGER.error(f"[INSPECT] Error: {error}", exc_info=True)
            except Exception:
                pass  # Logging failed, continue anyway
            
            # Use QTimer to ensure we're on the main thread - wrapped in try-except
            try:
                def show_dialog_safe():
                    """Safe dialog display wrapper"""
                    try:
                        self._show_error_dialog("Inspect Error", full_message, error_details)
                    except Exception as dialog_error:
                        # Even _show_error_dialog failed, try basic message box
                        try:
                            DEBUG_LOGGER.error(f"[INSPECT] _show_error_dialog failed: {dialog_error}", exc_info=True)
                            QMessageBox.critical(self, "Inspect Error", f"{full_message}\n\nError Code: {error_code}")
                        except Exception:
                            # Even basic message box failed, just log it
                            DEBUG_LOGGER.error(f"[INSPECT] All error dialog methods failed", exc_info=True)
                
                QTimer.singleShot(0, show_dialog_safe)
            except Exception as timer_error:
                # QTimer failed, try direct call (might be on main thread already)
                try:
                    DEBUG_LOGGER.error(f"[INSPECT] QTimer failed: {timer_error}", exc_info=True)
                    self._show_error_dialog("Inspect Error", full_message, error_details)
                except Exception:
                    # Even direct call failed, try basic message box
                    try:
                        QMessageBox.critical(self, "Inspect Error", f"{full_message}\n\nError Code: {error_code}")
                    except Exception:
                        DEBUG_LOGGER.error(f"[INSPECT] All error display methods failed", exc_info=True)
        except Exception as e:
            # ABSOLUTE CATCH-ALL: Even error dialog creation failed
            try:
                DEBUG_LOGGER.error(f"[INSPECT] CRITICAL: Failed to show error dialog: {e}", exc_info=True)
                # Last resort: try basic message box
                try:
                    QMessageBox.critical(self, "Critical Error", f"Inspect feature error:\n\n{str(e) if e else 'Unknown error'}")
                except Exception:
                    pass  # Everything failed, just log it
            except Exception:
                pass  # Even logging failed, give up
    
    def _show_error_dialog(self, title: str, message: str, error_code: str = ""):
        """Show error dialog with Copy Error Code and OK buttons - NEVER CRASHES"""
        try:
            dialog = QDialog(self)
            dialog.setWindowTitle(title)
            dialog.setMinimumWidth(500)
            dialog.setMinimumHeight(200)
            
            layout = QVBoxLayout(dialog)
            
            # Error message label
            msg_label = QLabel(message)
            msg_label.setWordWrap(True)
            msg_label.setStyleSheet("color: #e2e8f0; padding: 10px;")
            layout.addWidget(msg_label)
            
            # Error code text area (if provided)
            if error_code:
                code_label = QLabel("Error Code:")
                code_label.setStyleSheet("color: #94a3b8; font-weight: bold; margin-top: 10px;")
                layout.addWidget(code_label)
                
                code_text = QTextEdit()
                code_text.setPlainText(error_code)
                code_text.setReadOnly(True)
                code_text.setMaximumHeight(150)
                code_text.setStyleSheet("background: #1a1f2e; color: #cbd5e1; font-family: monospace; padding: 8px;")
                layout.addWidget(code_text)
            
            # Buttons
            button_layout = QHBoxLayout()
            
            if error_code:
                copy_btn = QPushButton("📋 Copy Error Code")
                copy_btn.setStyleSheet("""
                    QPushButton {
                        background: #3b82f6;
                        color: white;
                        padding: 8px 16px;
                        border-radius: 6px;
                        font-weight: 600;
                    }
                    QPushButton:hover {
                        background: #2563eb;
                    }
                """)
                def copy_error():
                    try:
                        clipboard = QApplication.clipboard()
                        clipboard.setText(error_code)
                        copy_btn.setText("✓ Copied!")
                        # Use a safe lambda that checks if the button still exists
                        def reset_button_text():
                            try:
                                # Check if button still exists before accessing it
                                if copy_btn and dialog.isVisible():
                                    copy_btn.setText("📋 Copy Error Code")
                            except (RuntimeError, AttributeError):
                                # Button was deleted or dialog closed - ignore
                                pass
                        QTimer.singleShot(2000, reset_button_text)
                    except Exception as e:
                        DEBUG_LOGGER.error(f"Error copying to clipboard: {e}")
                copy_btn.clicked.connect(copy_error)
                button_layout.addWidget(copy_btn)
            
            ok_btn = QPushButton("OK")
            ok_btn.setStyleSheet("""
                QPushButton {
                    background: #06b6d4;
                    color: white;
                    padding: 8px 24px;
                    border-radius: 6px;
                    font-weight: 600;
                }
                QPushButton:hover {
                    background: #0891b2;
                }
            """)
            ok_btn.clicked.connect(dialog.accept)
            button_layout.addWidget(ok_btn)
            
            layout.addLayout(button_layout)
            dialog.exec()
        except Exception as e:
            # Fallback to simple message box if dialog creation fails
            DEBUG_LOGGER.error(f"Error showing error dialog: {e}", exc_info=True)
            try:
                QMessageBox.critical(self, title, f"{message}\n\nError Code:\n{error_code}" if error_code else message)
            except:
                pass  # If even this fails, just log it
    
    def _setup_debug_log_handler(self):
        """Setup the GUI debug log handler"""
        gui_handler = GUIDebugLogHandler(self)
        DEBUG_LOGGER.addHandler(gui_handler)
        # Prevent DEBUG_LOGGER from propagating to root to avoid duplicate messages
        DEBUG_LOGGER.propagate = False
        # Also add to root logger to catch all module logs (but not DEBUG_LOGGER messages)
        root_logger = logging.getLogger()
        root_logger.addHandler(gui_handler)
        DEBUG_LOGGER.info("GUI debug log handler initialized")
    
    def _append_debug_log(self, message: str, level: str = "info"):
        """Append a message to the debug log with deduplication"""
        import time
        current_time = time.time()
        
        # Deduplication: Only show the same message once per 2 seconds
        if message in self._last_debug_log_messages:
            last_time = self._last_debug_log_messages[message]
            if current_time - last_time < 2.0:
                return  # Skip duplicate message
        
        # Update last seen time for this message
        self._last_debug_log_messages[message] = current_time
        
        # Clean up old entries (keep only last 100 messages)
        if len(self._last_debug_log_messages) > 100:
            # Remove entries older than 10 seconds
            cutoff_time = current_time - 10.0
            self._last_debug_log_messages = {msg: t for msg, t in self._last_debug_log_messages.items() if t > cutoff_time}
        
        from datetime import datetime
        timestamp = datetime.now().strftime("%H:%M:%S")
        # Escape message for JavaScript
        escaped_msg = message.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '').replace('</', '<\\/')
        js_code = f"""
            (function() {{
                const log = document.getElementById('debug-log');
                if (!log) return;
                const entry = document.createElement('div');
                entry.className = 'debug-log-entry {level}';
                entry.textContent = '[{timestamp}] {escaped_msg}';
                log.appendChild(entry);
                // Auto-scroll to bottom
                log.scrollTop = log.scrollHeight;
                // Limit to 500 entries to prevent memory issues
                while (log.children.length > 500) {{
                    log.removeChild(log.firstChild);
                }}
            }})();
        """
        self._execute_js(js_code)
    
    @pyqtSlot()
    def _process_js_queue(self):
        """Process JavaScript execution queue from main thread"""
        try:
            processed = 0
            while True:
                try:
                    script = self.js_queue.get_nowait()
                    if self.web_view:
                        self.web_view.page().runJavaScript(script)
                    processed += 1
                except queue.Empty:
                    break
                except Exception as e:
                    DEBUG_LOGGER.error(f"Error processing JS queue: {e}", exc_info=True)
                    break
        except Exception as e:
            DEBUG_LOGGER.error(f"CRITICAL error in _process_js_queue: {e}", exc_info=True)
    
    def _execute_js(self, script: str, callback=None):
        """Execute JavaScript in the web view (direct call, must be on main thread)"""
        try:
            if self.web_view is None:
                DEBUG_LOGGER.error("web_view is None - cannot execute JavaScript")
                return
            
            if callback:
                self.web_view.page().runJavaScript(script, callback)
            else:
                self.web_view.page().runJavaScript(script)
        except Exception as e:
            DEBUG_LOGGER.error(f"Failed to execute JavaScript: {e}", exc_info=True)
            raise
    
    def _execute_js_safe(self, script: str):
        """Thread-safe wrapper for _execute_js - can be called from any thread"""
        try:
            # Add script to queue - will be processed by main thread timer
            self.js_queue.put(script)
        except Exception as e:
            DEBUG_LOGGER.error(f"Failed to add script to queue: {e}", exc_info=True)
            # Don't raise - just log the error to prevent crashes
    
    def _append_terminal(self, text: str, tag: str = "output"):
        """Append text to terminal IMMEDIATELY - no batching for real-time display"""
        if not text:
            return
        
        import time
        text_lower = text.lower()
        text_stripped = text.strip()
        
        DEBUG_LOGGER.debug(f"[DETECT] Processing line: waiting_for_ai={self.waiting_for_ai}, text='{text_stripped[:100]}'")
        
        # Update last output time for any output
        if text_stripped:
            old_output_time = self.last_output_time
            self.last_output_time = time.time()
            DEBUG_LOGGER.debug(f"[TIMING] Output received: last_output_time={self.last_output_time:.3f} (was {old_output_time:.3f})")
            self._reset_inactivity_timeout()
        
        # Detect AI response start
        if text_stripped.startswith("AI>"):
            old_ai_time = self.last_ai_line_time
            self.waiting_for_ai = True
            self.last_ai_line_time = time.time()
            # Clear log cache when starting new AI response cycle
            self._last_log_times.clear()
            DEBUG_LOGGER.info(f"[AI_START] AI response detected: last_ai_line_time={self.last_ai_line_time:.3f} (was {old_ai_time:.3f}), waiting_for_ai={self.waiting_for_ai}")
            self._start_streaming_indicator()
            self._start_ai_timeout()
            self._reset_inactivity_timeout()
        
        # Detect when AI finishes - multiple patterns:
        # 1. Standalone "You>" or "You> " prompt (runner waiting for input)
        # 2. "[4DLLM] Chat ready" message (system ready)
        # 3. System output (performance metrics) after AI response
        is_you_prompt = (
            text_stripped == "You>" or 
            text_stripped == "You> " or
            (text_stripped.startswith("You> ") and len(text_stripped) <= 10 and not text_stripped.startswith("You> You>"))
        )
        is_ready_message = (
            "[4DLLM] Chat ready" in text_stripped or
            (text_stripped.startswith("[4DLLM]") and "ready" in text_lower)
        )
        
        # Detect system output after AI response (performance metrics)
        if self.waiting_for_ai and self.last_ai_line_time > 0:
            time_since_ai = time.time() - self.last_ai_line_time
            DEBUG_LOGGER.debug(f"[AI_CHECK] waiting_for_ai={self.waiting_for_ai}, last_ai_line_time={self.last_ai_line_time:.3f}, time_since_ai={time_since_ai:.3f}s")
            if time_since_ai > 2.0:
                is_system_output = (
                    "llama_perf" in text_lower or
                    "load time" in text_lower or
                    "eval time" in text_lower or
                    "total time" in text_lower or
                    "graphs reused" in text_lower or
                    text_stripped == "" or  # Empty line after AI response
                    (not text_stripped.startswith("AI>") and time_since_ai > 3.0)  # Any non-AI content after 3s
                )
                DEBUG_LOGGER.debug(f"[SYSTEM_CHECK] time_since_ai={time_since_ai:.3f}s, is_system_output={is_system_output}, text='{text_stripped[:50]}'")
                if is_system_output:
                    DEBUG_LOGGER.info(f"[AI_END] Detected AI response end (system output after {time_since_ai:.1f}s): '{text_stripped[:50]}'")
                    self.waiting_for_ai = False
                    self._stop_streaming_indicator()
                    self._cancel_ai_timeout()
                    self._cancel_inactivity_timeout()
                    self.last_ai_line_time = 0
        
        DEBUG_LOGGER.debug(f"[PROMPT_CHECK] is_you_prompt={is_you_prompt}, is_ready_message={is_ready_message}, text='{text_stripped[:50]}'")
        
        if is_you_prompt or is_ready_message:
            # AI finished responding - allow new messages
            if self.waiting_for_ai:
                DEBUG_LOGGER.info(f"[AI_END] AI finished responding (detected: '{text_stripped[:50]}'), ready for new input")
                self.waiting_for_ai = False
                self._stop_streaming_indicator()
                self._cancel_ai_timeout()
                self._cancel_inactivity_timeout()
                self.last_ai_line_time = 0
            else:
                DEBUG_LOGGER.debug(f"[PROMPT_CHECK] Prompt detected but waiting_for_ai=False (already reset?)")
        
        # Filter out duplicate "You> " prompts from the runner (but allow standalone "You>" for detection)
        if text_stripped == "You> " or text_stripped.startswith("You> You>"):
            # Skip auto-generated prompts (but we already detected it above)
            pass  # Don't skip - we need it for detection
        
        colors = {
            "output": "#e2e8f0",
            "success": "#34d399",
            "error": "#f87171",
            "warning": "#fbbf24",
            "info": "#60a5fa",
            "command": "#3b82f6"
        }
        color = colors.get(tag, "#e2e8f0")
        
        # Escape text properly for JavaScript (handle special chars)
        escaped_text = json.dumps(text)
        
        # Direct DOM update - no batching for instant display
        script = f"""
            (function() {{
                const output = document.getElementById('terminal-output');
                if (!output) {{ console.error('Terminal output element not found!'); return; }}
                
                // Remove streaming indicator if present
                const existingIndicator = output.querySelector('.streaming-indicator');
                if (existingIndicator) {{
                    existingIndicator.remove();
                }}
                
                const span = document.createElement('span');
                span.style.color = '{color}';
                span.textContent = {escaped_text};
                output.appendChild(span);
                
                // Always auto-scroll for real-time updates
                output.scrollTop = output.scrollHeight;
            }})();
        """
        # Execute immediately - don't queue
        self.web_view.page().runJavaScript(script)
    
    def _start_streaming_indicator(self):
        """Start showing streaming dots like Ollama"""
        if self.streaming_indicator:
            return
        
        self._dot_count = 0
        
        def update_dots():
            if not self.waiting_for_ai:
                self._stop_streaming_indicator()
                return
            
            self._dot_count = (self._dot_count + 1) % 3
            dots = "." * (self._dot_count + 1)
            
            script = f"""
                (function() {{
                    const output = document.getElementById('terminal-output');
                    if (!output) return;
                    
                    // Remove old indicator
                    const old = output.querySelector('.streaming-indicator');
                    if (old) old.remove();
                    
                    // Add new indicator
                    const span = document.createElement('span');
                    span.className = 'streaming-indicator';
                    span.style.color = '#60a5fa';
                    span.textContent = '{dots}';
                    output.appendChild(span);
                    output.scrollTop = output.scrollHeight;
                }})();
            """
            self.web_view.page().runJavaScript(script)
            
            # Schedule next update
            if self.waiting_for_ai:
                QTimer.singleShot(500, update_dots)
        
        # Start the animation
        update_dots()
    
    def _stop_streaming_indicator(self):
        """Stop streaming indicator"""
        if self.streaming_indicator:
            try:
                QTimer.singleShot(0, lambda: None)  # Cancel if possible
            except:
                pass
            self.streaming_indicator = None
        
        # Remove indicator from DOM
        script = """
            (function() {
                const output = document.getElementById('terminal-output');
                if (!output) return;
                const indicator = output.querySelector('.streaming-indicator');
                if (indicator) indicator.remove();
            })();
        """
        self.web_view.page().runJavaScript(script)
    
    def _start_ai_timeout(self):
        """Start a timeout to reset waiting state if detection fails"""
        self._cancel_ai_timeout()
        DEBUG_LOGGER.debug(f"[TIMEOUT] Starting AI timeout (30s) - waiting_for_ai={self.waiting_for_ai}")
        # Set timeout for 30 seconds - if no "You>" prompt detected, reset anyway
        def timeout_reset():
            if self.waiting_for_ai:
                DEBUG_LOGGER.warning(f"[TIMEOUT] AI timeout (30s): resetting waiting state (no 'You>' prompt detected)")
                DEBUG_LOGGER.warning(f"[TIMEOUT] State at timeout: last_output_time={self.last_output_time:.3f}, last_ai_line_time={self.last_ai_line_time:.3f}")
                self.waiting_for_ai = False
                self._stop_streaming_indicator()
            else:
                DEBUG_LOGGER.debug(f"[TIMEOUT] AI timeout triggered but waiting_for_ai=False (already reset)")
        
        # Use QTimer object for proper cancellation
        self.ai_timeout_id = QTimer()
        self.ai_timeout_id.setSingleShot(True)
        self.ai_timeout_id.timeout.connect(timeout_reset)
        self.ai_timeout_id.start(30000)  # 30 second timeout
    
    def _cancel_ai_timeout(self):
        """Cancel the AI timeout"""
        if self.ai_timeout_id:
            try:
                self.ai_timeout_id.stop()
                self.ai_timeout_id.deleteLater()
            except:
                pass
            self.ai_timeout_id = None
    
    def _reset_inactivity_timeout(self):
        """Reset the inactivity timeout - called when we see output"""
        self._cancel_inactivity_timeout()
        if not self.waiting_for_ai:
            DEBUG_LOGGER.debug(f"[TIMEOUT] Not resetting inactivity timeout - waiting_for_ai=False")
            return
        
        DEBUG_LOGGER.debug(f"[TIMEOUT] Resetting inactivity timeout - last_output_time={self.last_output_time:.3f}")
        
        # If no output for 3 seconds, assume AI finished (even without "You>" prompt)
        def inactivity_check():
            if not self.waiting_for_ai:
                DEBUG_LOGGER.debug(f"[TIMEOUT] Inactivity check skipped - waiting_for_ai=False")
                return
            
            import time
            current_time = time.time()
            time_since_output = current_time - self.last_output_time
            time_since_ai = current_time - self.last_ai_line_time if self.last_ai_line_time > 0 else 999
            
            # Deduplication: Use generic message type key to prevent duplicate logs
            check_log_key = "timeout_inactivity_check"
            if check_log_key not in self._last_log_times or (current_time - self._last_log_times[check_log_key]) >= 2.0:
                DEBUG_LOGGER.debug(f"[TIMEOUT] Inactivity check: time_since_output={time_since_output:.3f}s, time_since_ai={time_since_ai:.3f}s")
                self._last_log_times[check_log_key] = current_time
            
            # If no output for 3 seconds OR no AI output for 3 seconds, reset
            if time_since_output >= 2.5 or (time_since_ai >= 2.5 and self.last_ai_line_time > 0):
                # Deduplication: Use generic message type key
                inactivity_log_key = "timeout_inactivity_detected"
                if inactivity_log_key not in self._last_log_times or (current_time - self._last_log_times[inactivity_log_key]) >= 2.0:
                    DEBUG_LOGGER.warning(f"[TIMEOUT] Inactivity detected (output: {time_since_output:.1f}s, AI: {time_since_ai:.1f}s): resetting waiting state")
                    self._last_log_times[inactivity_log_key] = current_time
                self.waiting_for_ai = False
                self._stop_streaming_indicator()
                self._cancel_ai_timeout()
                self.last_ai_line_time = 0
            else:
                # Check again in 500ms for faster response
                DEBUG_LOGGER.debug(f"[TIMEOUT] Scheduling next inactivity check in 500ms")
                self.inactivity_timeout_id = QTimer()
                self.inactivity_timeout_id.setSingleShot(True)
                self.inactivity_timeout_id.timeout.connect(inactivity_check)
                self.inactivity_timeout_id.start(500)
        
        # Start checking after 3 seconds of inactivity (shorter for faster recovery)
        DEBUG_LOGGER.debug(f"[TIMEOUT] Starting inactivity timeout - will check in 3 seconds")
        self.inactivity_timeout_id = QTimer()
        self.inactivity_timeout_id.setSingleShot(True)
        self.inactivity_timeout_id.timeout.connect(inactivity_check)
        self.inactivity_timeout_id.start(3000)
    
    def _cancel_inactivity_timeout(self):
        """Cancel the inactivity timeout"""
        if self.inactivity_timeout_id:
            try:
                self.inactivity_timeout_id.stop()
                self.inactivity_timeout_id.deleteLater()
            except:
                pass
            self.inactivity_timeout_id = None
    
    def _open_4dllm(self):
        """Open 4DLLM file - safe with error handling (also supports GGUF for universal access)"""
        try:
            DEBUG_LOGGER.info("Opening file dialog (supports both .4dllm and .gguf)...")
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Open Model File", "", 
                "Model Files (*.4dllm *.gguf);;4DLLM Files (*.4dllm);;GGUF Files (*.gguf);;All Files (*)"
            )
            if file_path:
                # Unload training model if one is loaded
                self._unload_other_models(exclude_section="run")
                
                file_path_obj = Path(file_path)
                DEBUG_LOGGER.info(f"Selected file: {file_path_obj}")
                
                # Validate file exists
                if not file_path_obj.exists():
                    self._show_error_dialog("File Not Found", f"The selected file does not exist:\n{file_path_obj}", "ERR-RUNNER-FILE-NOT-FOUND-001")
                    return
                
                # Determine file type from extension
                file_ext = file_path_obj.suffix.lower()
                is_gguf = file_ext == '.gguf'
                is_4dllm = file_ext == '.4dllm'
                
                if not (is_gguf or is_4dllm):
                    self._show_error_dialog("Unsupported File Type", 
                                      f"Selected file must be a .4dllm or .gguf file.\nGot: {file_ext}", "ERR-RUNNER-UNSUPPORTED-TYPE-001")
                    return
                
                # Set file path and type
                self.run_path = file_path_obj
                self.is_gguf_file = is_gguf
                
                # Also update gguf_path if it's a GGUF file (for compatibility with build tab)
                if is_gguf:
                    self.gguf_path = file_path_obj
                
                # Update UI safely
                try:
                    file_name_escaped = file_path_obj.name.replace("'", "\\'").replace("\\", "\\\\")
                    file_type_label = 'GGUF' if is_gguf else '4DLLM'
                    file_type_escaped = file_type_label.replace("'", "\\'")
                    update_js = f"""
                        const el = document.getElementById('selected-file');
                        el.innerHTML = '{file_name_escaped}<br><span style="font-size: 11px; color: #64748b;">({file_type_escaped} file)</span>';
                    """
                    self._execute_js(update_js)
                    DEBUG_LOGGER.info(f"Updated UI with file: {file_path_obj.name} (type: {file_type_label})")
                except Exception as ui_err:
                    DEBUG_LOGGER.error(f"Error updating UI: {ui_err}", exc_info=True)
                    # Still set the path even if UI update fails
                
                # Add to recent files (in background to prevent freeze)
                def add_to_recent():
                    try:
                        recent_files_js = f"""
                            const recentEl = document.getElementById('recent-files');
                            if (recentEl) {{
                                const currentText = recentEl.textContent || '';
                                const fileName = {json.dumps(file_path_obj.name)};
                                if (!currentText.includes(fileName)) {{
                                    recentEl.textContent = (currentText.trim() ? currentText + '\\n' : '') + fileName;
                                }}
                            }}
                        """
                        QTimer.singleShot(0, lambda: self._execute_js(recent_files_js))
                    except Exception as e:
                        DEBUG_LOGGER.error(f"Error adding to recent files: {e}")
                
                # Run in background thread
                import threading
                threading.Thread(target=add_to_recent, daemon=True).start()
                
        except Exception as e:
            import traceback
            DEBUG_LOGGER.error(f"Error in _open_4dllm: {e}", exc_info=True)
            self._show_error_dialog("Error", f"Failed to open 4DLLM file:\n{e}", f"ERR-RUNNER-OPEN-4DLLM-001\n\n{traceback.format_exc()}")
    
    def _open_gguf(self):
        """Open GGUF file - safe with error handling (also supports 4DLLM for universal access)"""
        try:
            DEBUG_LOGGER.info("Opening file dialog (supports both .4dllm and .gguf)...")
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Open Model File", "", 
                "Model Files (*.4dllm *.gguf);;GGUF Files (*.gguf);;4DLLM Files (*.4dllm);;All Files (*)"
            )
            if file_path:
                # Unload training model if one is loaded
                self._unload_other_models(exclude_section="run")
                
                file_path_obj = Path(file_path)
                DEBUG_LOGGER.info(f"Selected file: {file_path_obj}")
                
                # Validate file exists
                if not file_path_obj.exists():
                    self._show_error_dialog("File Not Found", f"The selected file does not exist:\n{file_path_obj}", "ERR-RUNNER-FILE-NOT-FOUND-001")
                    return
                
                # Determine file type from extension
                file_ext = file_path_obj.suffix.lower()
                is_gguf = file_ext == '.gguf'
                is_4dllm = file_ext == '.4dllm'
                
                if not (is_gguf or is_4dllm):
                    self._show_error_dialog("Unsupported File Type", 
                                      f"Selected file must be a .4dllm or .gguf file.\nGot: {file_ext}", "ERR-RUNNER-UNSUPPORTED-TYPE-001")
                    return
                
                # Set file paths (for both runner and build tab)
                self.run_path = file_path_obj
                self.is_gguf_file = is_gguf
                
                # Also update gguf_path if it's a GGUF file (for compatibility with build tab)
                if is_gguf:
                    self.gguf_path = file_path_obj
                
                # Update UI safely
                try:
                    file_name_escaped = file_path_obj.name.replace("'", "\\'").replace("\\", "\\\\")
                    file_type_label = 'GGUF' if is_gguf else '4DLLM'
                    file_type_escaped = file_type_label.replace("'", "\\'")
                    # Update selected file display (for Run tab)
                    update_js = f"""
                        const el = document.getElementById('selected-file');
                        if (el) {{
                            el.innerHTML = '{file_name_escaped}<br><span style="font-size: 11px; color: #64748b;">({file_type_escaped} file)</span>';
                        }}
                    """
                    self._execute_js(update_js)
                    # Also update if there's a gguf-file element
                    self._execute_js(f"""
                        const ggufEl = document.getElementById('gguf-file');
                        if (ggufEl) {{
                            ggufEl.textContent = '{file_name_escaped}';
                        }}
                    """)
                    DEBUG_LOGGER.info(f"Updated UI with file: {file_path_obj.name} (type: {file_type_label})")
                except Exception as ui_err:
                    DEBUG_LOGGER.error(f"Error updating UI: {ui_err}", exc_info=True)
                
                # Add to recent files (in background to prevent freeze)
                def add_to_recent():
                    try:
                        recent_files_js = f"""
                            const recentEl = document.getElementById('recent-files');
                            if (recentEl) {{
                                const currentText = recentEl.textContent || '';
                                const fileName = {json.dumps(file_path_obj.name)};
                                if (!currentText.includes(fileName)) {{
                                    recentEl.textContent = (currentText.trim() ? currentText + '\\n' : '') + fileName;
                                }}
                            }}
                        """
                        QTimer.singleShot(0, lambda: self._execute_js(recent_files_js))
                    except Exception as e:
                        DEBUG_LOGGER.error(f"Error adding to recent files: {e}")
                
                # Run in background thread
                import threading
                threading.Thread(target=add_to_recent, daemon=True).start()
                
        except Exception as e:
            import traceback
            DEBUG_LOGGER.error(f"Error in _open_gguf: {e}", exc_info=True)
            self._show_error_dialog("Error", f"Failed to open GGUF file:\n{e}", f"ERR-RUNNER-OPEN-GGUF-001\n\n{traceback.format_exc()}")
    
    def _select_file(self):
        """Select file for running - supports both .4dllm and .gguf"""
        try:
            DEBUG_LOGGER.info("Opening file dialog for runner (supports .4dllm and .gguf)...")
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Open Model File", "", 
                "Model Files (*.4dllm *.gguf);;4DLLM Files (*.4dllm);;GGUF Files (*.gguf);;All Files (*)"
            )
            if file_path:
                # Unload training model if one is loaded
                self._unload_other_models(exclude_section="run")
                
                file_path_obj = Path(file_path)
                DEBUG_LOGGER.info(f"Selected file for running: {file_path_obj}")
                
                # Validate file exists
                if not file_path_obj.exists():
                    self._show_error_dialog("File Not Found", f"The selected file does not exist:\n{file_path_obj}", "ERR-RUNNER-FILE-NOT-FOUND-001")
                    return
                
                # Determine file type
                file_ext = file_path_obj.suffix.lower()
                is_gguf = file_ext == '.gguf'
                is_4dllm = file_ext == '.4dllm'
                
                if not (is_gguf or is_4dllm):
                    self._show_error_dialog("Unsupported File Type", 
                                      f"Selected file must be a .4dllm or .gguf file.\nGot: {file_ext}", "ERR-RUNNER-UNSUPPORTED-TYPE-001")
                    return
                
                # Set file path and type
                self.run_path = file_path_obj
                self.is_gguf_file = is_gguf
                
                # Also update gguf_path if it's a GGUF file (for compatibility with build tab)
                if is_gguf:
                    self.gguf_path = file_path_obj
                
                # Update UI safely
                try:
                    file_name_escaped = file_path_obj.name.replace("'", "\\'").replace("\\", "\\\\")
                    file_type_label = 'GGUF' if is_gguf else '4DLLM'
                    file_type_escaped = file_type_label.replace("'", "\\'")
                    update_js = f"""
                        const el = document.getElementById('selected-file');
                        el.innerHTML = '{file_name_escaped}<br><span style="font-size: 11px; color: #64748b;">({file_type_escaped} file)</span>';
                    """
                    self._execute_js(update_js)
                    DEBUG_LOGGER.info(f"Updated UI with file: {file_path_obj.name} (type: {file_type_label})")
                except Exception as ui_err:
                    DEBUG_LOGGER.error(f"Error updating UI: {ui_err}", exc_info=True)
                
                # Add to recent files (in background to prevent freeze)
                def add_to_recent():
                    try:
                        recent_files_js = f"""
                            const recentEl = document.getElementById('recent-files');
                            if (recentEl) {{
                                const currentText = recentEl.textContent || '';
                                const fileName = {json.dumps(file_path_obj.name)};
                                if (!currentText.includes(fileName)) {{
                                    recentEl.textContent = (currentText.trim() ? currentText + '\\n' : '') + fileName;
                                }}
                            }}
                        """
                        QTimer.singleShot(0, lambda: self._execute_js(recent_files_js))
                    except Exception as e:
                        DEBUG_LOGGER.error(f"Error adding to recent files: {e}")
                
                # Run in background thread
                import threading
                threading.Thread(target=add_to_recent, daemon=True).start()
                
        except Exception as e:
            import traceback
            DEBUG_LOGGER.error(f"Error selecting file: {e}", exc_info=True)
            self._show_error_dialog("Error", f"Failed to select file:\n{e}", f"ERR-RUNNER-SELECT-FILE-001\n\n{traceback.format_exc()}")
    
    def _run_model(self):
        # Stop any currently running model first
        if self.proc:
            DEBUG_LOGGER.info("Stopping currently running model before starting new one...")
            self._stop_model()
        
        if not self.run_path:
            self._show_error_dialog("No File Selected", "Please select a .4dllm or .gguf file first.", "ERR-RUNNER-NO-FILE-001")
            return
        
        # Clear terminal
        self._execute_js("document.getElementById('terminal-output').textContent = '';")
        
        cmd = self._build_command()
        env = os.environ.copy()
        env['PYTHONUNBUFFERED'] = '1'
        env['PYTHONIOENCODING'] = 'utf-8'
        
        # Ensure unbuffered Python
        if cmd[0] == sys.executable:
            cmd = [cmd[0], '-u'] + cmd[1:]
        
        try:
            self._append_terminal(f"Starting: {' '.join(shlex.quote(str(arg)) for arg in cmd)}\n", "info")
            
            # CRITICAL: Line-buffered for immediate output
            self.proc = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                stdin=subprocess.PIPE,
                text=True,
                bufsize=1,  # Line buffered for immediate output
                env=env,
                universal_newlines=True,
                cwd=str(Path(__file__).parent)
            )
            
            # Make stdout non-blocking for faster reads (Unix only)
            if HAS_FCNTL and sys.platform != "win32":
                try:
                    flags = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFL)
                    fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
                except Exception as e:
                    DEBUG_LOGGER.debug(f"Could not set non-blocking mode: {e}")
            
            # Start reader thread
            self.reader_thread = OutputReaderThread(self.proc)
            self.reader_thread.outputReceived.connect(self._append_terminal)
            self.reader_thread.finished.connect(self._on_process_finished)
            self.reader_thread.start()
            
            self._append_terminal("Process started. Waiting for output...\n", "info")
        except Exception as e:
            import traceback
            error_msg = f"Failed to start process: {str(e)}"
            DEBUG_LOGGER.error(error_msg, exc_info=True)
            self._show_error_dialog("Run Failed", error_msg, f"ERR-RUNNER-START-001\n\n{traceback.format_exc()}")
            self._append_terminal(f"ERROR: {error_msg}\n", "error")
    
    def _unload_other_models(self, exclude_section: str = "run"):
        """
        Unload models from other sections when loading a model in a specific section.
        Args:
            exclude_section: "run" to unload training model, "train" to unload run model
        """
        try:
            if exclude_section == "run":
                # Unloading for Run section - stop training if running
                if self.is_training and self.training_process:
                    DEBUG_LOGGER.info("Unloading training model (stopping training process)...")
                    self._stop_training()
                
                # Clear training model path
                if self.train_input_file:
                    DEBUG_LOGGER.info(f"Clearing training model: {self.train_input_file}")
                    self.train_input_file = None
                    # Update UI to show no model loaded
                    self._execute_js("""
                        const el = document.getElementById('train-input-file');
                        if (el) el.textContent = 'No file selected';
                        const statusEl = document.getElementById('train-model-status');
                        if (statusEl) statusEl.style.display = 'none';
                    """)
                    
            elif exclude_section == "train":
                # Unloading for Train section - stop run model if running
                if self.proc:
                    DEBUG_LOGGER.info("Unloading run model (stopping run process)...")
                    self._stop_model()
                
                # Clear run model path
                if self.run_path:
                    DEBUG_LOGGER.info(f"Clearing run model: {self.run_path}")
                    self.run_path = None
                    # Update UI to show no model selected
                    self._execute_js("""
                        const el = document.getElementById('selected-file');
                        if (el) el.innerHTML = 'No file selected';
                    """)
                    
        except Exception as e:
            DEBUG_LOGGER.error(f"Error unloading other models: {e}", exc_info=True)
    
    def _stop_model(self):
        if not self.proc:
            return
        
        try:
            # Stop reader thread first
            if self.reader_thread:
                self.reader_thread.stop()
                self.reader_thread.wait(1000)  # Wait up to 1 second
            
            # Close stdin to signal termination
            if self.proc.stdin:
                try:
                    self.proc.stdin.close()
                except:
                    pass
            
            # Terminate process
            self.proc.terminate()
            time.sleep(0.5)
            
            # Force kill if still running
            if self.proc.poll() is None:
                self.proc.kill()
                time.sleep(0.1)
            
            self._append_terminal("\n[Process terminated]\n", "warning")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error stopping process: {e}", exc_info=True)
        
        # Always clean up state
        self._stop_streaming_indicator()
        self._cancel_ai_timeout()
        self._cancel_inactivity_timeout()
        self.waiting_for_ai = False
        self.last_ai_line_time = 0
        self.proc = None
        self.reader_thread = None
    
    def _copy_command(self):
        if not self.run_path:
            QMessageBox.warning(self, "No File", "Select a .4dllm or .gguf file first.")
            return
        cmd = self._build_command()
        # For GGUF files, the command includes a temp script path which isn't useful to copy
        # So we'll provide a simplified version
        if self.is_gguf_file:
            cmd_str = f"# GGUF file: {self.run_path}\n# Use llama-cpp-python to load and run this file"
            QApplication.clipboard().setText(cmd_str)
            QMessageBox.information(self, "Copied", "GGUF file path copied to clipboard!\nNote: GGUF files require llama-cpp-python to run.")
        else:
            cmd_str = " ".join(shlex.quote(str(arg)) for arg in cmd)
            QApplication.clipboard().setText(cmd_str)
            QMessageBox.information(self, "Copied", "Command copied to clipboard!")
    
    def _select_gguf(self):
        self._open_gguf()
        if self.gguf_path:
            # Update build tab GGUF display
            self._execute_js(f"document.getElementById('build-gguf-file').textContent = '{self.gguf_path.name}';")
    
    def _build_package(self):
        """Build 4DLLM package using embedded builder"""
        
        # ========== VERBOSE DEBUG LOGGING ==========
        build_id = f"build_{int(time.time() * 1000)}"
        BUILDER_DEBUG.log_build_attempt(build_id, {
            "action": "build_package_called",
            "gguf_path": str(self.gguf_path) if self.gguf_path else None,
            "python_storage_count": len(self.builder_scripts),
            "python_storage_scripts": [s.get("name", "N/A") for s in self.builder_scripts]
        })
        BUILDER_DEBUG.snapshot_python_storage(self.builder_scripts, f"build_start_{build_id}")
        
        if not self.gguf_path or not self.gguf_path.exists():
            BUILDER_DEBUG.log_error("build_no_gguf", {
                "build_id": build_id,
                "gguf_path": str(self.gguf_path) if self.gguf_path else None
            })
            QMessageBox.warning(self, "Error", "Please select a GGUF file first!")
            return
        
        # Get output path from UI
        output_path = None
        def get_output_path():
            nonlocal output_path
            script = "document.getElementById('build-output-path').value;"
            self.web_view.page().runJavaScript(script, lambda result: setattr(self, '_js_result', result))
            time.sleep(0.1)  # Wait for JS execution
            output_path = getattr(self, '_js_result', None) if hasattr(self, '_js_result') else None
        
        get_output_path()
        
        # Only prompt once if not set in UI
        if not output_path or not output_path.strip():
            # Suggest default name based on GGUF file
            default_name = self.gguf_path.stem + ".4dllm"
            default_path = str(self.gguf_path.parent / default_name)
            output_path, _ = QFileDialog.getSaveFileName(
                self, "Save 4DLLM File", default_path, "4DLLM Files (*.4dllm);;All Files (*)"
            )
            if not output_path:
                return
            # Update UI with selected path so it's remembered
            escaped_path = output_path.replace('\\', '\\\\').replace("'", "\\'")
            self._execute_js(f"document.getElementById('build-output-path').value = '{escaped_path}';")
        
        # Get compression settings
        compress_scripts = True
        compress_metadata = True
        
        try:
            self._append_terminal(f"Building 4DLLM package...\n", "info")
            self._append_terminal(f"GGUF: {self.gguf_path.name}\n", "info")
            self._append_terminal(f"Output: {output_path}\n", "info")
            
            # Create builder
            builder = builder_module.FourDLLMBuilderV2(str(self.gguf_path))
            
            # Get system prompt from UI - robust, logged
            DEBUG_LOGGER.info("BUILD: capturing system prompt from UI...")
            system_prompt = ""
            system_prompt_status = "System prompt: not provided"
            try:
                # Use execute_js with callback for synchronous retrieval (robust event loop)
                system_prompt_result = [None]  # Use list to allow modification in closure
                
                loop = QEventLoop()
                timeout_timer = QTimer()
                timeout_timer.setSingleShot(True)
                timeout_timer.timeout.connect(loop.quit)
                
                def set_system_prompt_result(result):
                    system_prompt_result[0] = result if result else ""
                    loop.quit()
                
                script_primary = "document.getElementById('build-system-prompt') ? document.getElementById('build-system-prompt').value || '' : '';"
                self.web_view.page().runJavaScript(script_primary, set_system_prompt_result)
                
                timeout_timer.start(1500)  # up to 1.5s to allow page/JS to respond
                loop.exec()
                timeout_timer.stop()
                
                system_prompt = system_prompt_result[0] if system_prompt_result[0] is not None else ""
                system_prompt_clean_preview = system_prompt.strip() if isinstance(system_prompt, str) else ""
                DEBUG_LOGGER.info(f"BUILD: system prompt read (primary) length={len(system_prompt_clean_preview) if system_prompt_clean_preview else 0}")
                
                # Second attempt if empty: broader selector
                if not system_prompt_clean_preview:
                    system_prompt_result[0] = None
                    loop = QEventLoop()
                    timeout_timer = QTimer()
                    timeout_timer.setSingleShot(True)
                    timeout_timer.timeout.connect(loop.quit)
                    def set_system_prompt_result_2(result):
                        system_prompt_result[0] = result if result else ""
                        loop.quit()
                    script_alt = "(function(){ const el=document.querySelector('textarea[id*=\"system-prompt\"]'); return el ? el.value || '' : ''; })();"
                    self.web_view.page().runJavaScript(script_alt, set_system_prompt_result_2)
                    timeout_timer.start(1500)
                    loop.exec()
                    timeout_timer.stop()
                    system_prompt = system_prompt_result[0] if system_prompt_result[0] is not None else ""
                system_prompt_clean_preview = system_prompt.strip() if isinstance(system_prompt, str) else ""
                DEBUG_LOGGER.info(f"BUILD: system prompt read (fallback) length={len(system_prompt_clean_preview) if system_prompt_clean_preview else 0}")
                
                # Third attempt: grab first textarea value on page (last resort)
                if not system_prompt_clean_preview:
                    system_prompt_result[0] = None
                    loop = QEventLoop()
                    timeout_timer = QTimer()
                    timeout_timer.setSingleShot(True)
                    timeout_timer.timeout.connect(loop.quit)
                    def set_system_prompt_result_3(result):
                        system_prompt_result[0] = result if result else ""
                        loop.quit()
                    script_any = "(function(){ const el=document.querySelector('textarea'); return el ? el.value || '' : ''; })();"
                    self.web_view.page().runJavaScript(script_any, set_system_prompt_result_3)
                    timeout_timer.start(1500)
                    loop.exec()
                    timeout_timer.stop()
                    system_prompt = system_prompt_result[0] if system_prompt_result[0] is not None else ""
                    system_prompt_clean_preview = system_prompt.strip() if isinstance(system_prompt, str) else ""
                    DEBUG_LOGGER.info(f"BUILD: system prompt read (any-textarea) length={len(system_prompt_clean_preview) if system_prompt_clean_preview else 0}")
                
                # Fourth attempt: read saved localStorage prompt if still empty
                if not system_prompt_clean_preview:
                    system_prompt_result[0] = None
                    loop = QEventLoop()
                    timeout_timer = QTimer()
                    timeout_timer.setSingleShot(True)
                    timeout_timer.timeout.connect(loop.quit)
                    def set_system_prompt_result_4(result):
                        system_prompt_result[0] = result if result else ""
                        loop.quit()
                    script_ls = "(function(){ try { return localStorage.getItem('builder_system_prompt') || ''; } catch(e) { return ''; } })();"
                    self.web_view.page().runJavaScript(script_ls, set_system_prompt_result_4)
                    timeout_timer.start(1500)
                    loop.exec()
                    timeout_timer.stop()
                    system_prompt = system_prompt_result[0] if system_prompt_result[0] is not None else ""
                    system_prompt_clean_preview = system_prompt.strip() if isinstance(system_prompt, str) else ""
                    DEBUG_LOGGER.info(f"BUILD: system prompt read (localStorage) length={len(system_prompt_clean_preview) if system_prompt_clean_preview else 0}")
                
                # Fifth attempt: read saved builder prompt file directly
                if not system_prompt_clean_preview and hasattr(self, 'builder_prompt_file'):
                    try:
                        if self.builder_prompt_file.exists():
                            data = json.loads(self.builder_prompt_file.read_text(encoding="utf-8"))
                            file_prompt = data.get("system_prompt", "") if isinstance(data, dict) else ""
                            if file_prompt:
                                system_prompt = file_prompt
                                system_prompt_clean_preview = system_prompt.strip()
                                DEBUG_LOGGER.info(f"BUILD: system prompt read (file) length={len(system_prompt_clean_preview)}")
                    except Exception as file_err:
                        DEBUG_LOGGER.warning(f"BUILD: failed to read builder prompt file: {file_err}")
                
                user_provided_prompt = bool(system_prompt_clean_preview)
                if user_provided_prompt:
                    system_prompt_status = f"System prompt: saved ({len(system_prompt_clean_preview)} chars)"
                    self._last_system_prompt = system_prompt_clean_preview if hasattr(self, '_last_system_prompt') else system_prompt_clean_preview
                else:
                    # Fallback to last known prompt if UI read was empty
                    if hasattr(self, '_last_system_prompt') and self._last_system_prompt:
                        system_prompt = self._last_system_prompt
                        system_prompt_status = f"System prompt: saved ({len(system_prompt)} chars) [restored]"
                        DEBUG_LOGGER.info("BUILD: system prompt restored from last known cache")
                    elif DEFAULT_SYSTEM_PROMPT:
                        # Use default SimpleForge-style prompt when nothing provided
                        system_prompt = DEFAULT_SYSTEM_PROMPT
                        system_prompt_status = f"System prompt: default applied ({len(system_prompt)} chars)"
                        DEBUG_LOGGER.info("BUILD: system prompt set to default fallback (SimpleForge style)")
                preview_log = ""
                if isinstance(system_prompt, str) and system_prompt:
                    preview_log = system_prompt[:120].replace("\n", "\\n")
                DEBUG_LOGGER.info(f"BUILD: final system prompt length={len(system_prompt) if isinstance(system_prompt, str) else 0} preview='{preview_log}'")
            except Exception as e:
                DEBUG_LOGGER.warning(f"Failed to retrieve system prompt from UI: {e}")
                system_prompt = ""
            
            # Set metadata (same as standalone builder)
            builder.metadata = {
                "name": "RomanAILabs 4DLLM",
                "description": "GGUF + injected python modules (4DLLM v2)",
                "modules_dir": str(self.modules_dir),
                "gguf": {"path": str(self.gguf_path), "name": self.gguf_path.name},
            }
            
            # Add system prompt to metadata if provided (all compatible keys)
            if system_prompt and system_prompt.strip():
                system_prompt_clean = system_prompt.strip()
                builder.metadata["system_prompt"] = system_prompt_clean  # modelfile-style key
                builder.metadata["llama.system_prompt"] = system_prompt_clean  # llama.cpp compatibility
                builder.metadata["chat_template.system_prompt"] = system_prompt_clean  # chat template compatibility
                DEBUG_LOGGER.info(f"BUILD: system prompt added to metadata (len={len(system_prompt_clean)}) keys=['system_prompt','llama.system_prompt','chat_template.system_prompt']")
                system_prompt_status = f"System prompt: saved ({len(system_prompt_clean)} chars)"
                self._last_system_prompt = system_prompt_clean if hasattr(self, '_last_system_prompt') else system_prompt_clean
            else:
                DEBUG_LOGGER.info("No system prompt provided - metadata will not include system prompt")
            
            # Get scripts from Python storage (like standalone builder) - NO JavaScript async issues!
            scripts_data = []
            DEBUG_LOGGER.info("=" * 80)
            DEBUG_LOGGER.info("BUILD: Retrieving scripts from Python storage (self.builder_scripts)")
            DEBUG_LOGGER.info(f"BUILD: Python storage has {len(self.builder_scripts)} script(s)")
            
            BUILDER_DEBUG.log_event("build_retrieve_python_storage", {
                "build_id": build_id,
                "python_storage_count": len(self.builder_scripts),
                "python_storage_scripts": [{"name": s.get("name"), "enabled": s.get("enabled"), "content_len": len(s.get("content", ""))} for s in self.builder_scripts]
            })
            
            # Use Python storage directly - this is the source of truth
            scripts_data = self.builder_scripts.copy()
            DEBUG_LOGGER.info(f"BUILD: Retrieved {len(scripts_data)} script(s) from Python storage")
            
            BUILDER_DEBUG.log_event("build_python_storage_retrieved", {
                "build_id": build_id,
                "retrieved_count": len(scripts_data),
                "scripts": [{"name": s.get("name"), "enabled": s.get("enabled"), "content_len": len(s.get("content", ""))} for s in scripts_data]
            })
            
            # Log each script
            for i, script in enumerate(scripts_data):
                DEBUG_LOGGER.info(f"BUILD: Script {i+1}: name='{script.get('name', 'N/A')}', "
                                f"content_len={len(script.get('content', ''))}, "
                                f"enabled={script.get('enabled', True)}")
            
            # Fallback: If Python storage is empty, try JavaScript (for backwards compatibility)
            # Also sync JavaScript scripts to Python storage for next time
            if not scripts_data:
                DEBUG_LOGGER.warning("BUILD: Python storage empty, falling back to JavaScript retrieval...")
                DEBUG_LOGGER.info("BUILD: Will sync JavaScript scripts to Python storage after retrieval")
                
                BUILDER_DEBUG.log_event("build_python_storage_empty", {
                    "build_id": build_id,
                    "fallback_to": "javascript",
                    "reason": "python_storage_empty"
                })
                
                try:
                    # Use execute_js with callback for synchronous retrieval
                    scripts_result = [None]  # Use list to allow modification in closure
                    callback_called = [False]
                    
                    def set_scripts_result(result):
                        callback_called[0] = True
                        scripts_result[0] = result if result else None
                        DEBUG_LOGGER.info(f"BUILD: JavaScript callback executed. Result type: {type(result)}, Result length: {len(str(result)) if result else 0}")
                        if result:
                            DEBUG_LOGGER.info(f"BUILD: Result preview (first 500 chars): {str(result)[:500]}")
                    
                    # Get scripts from JavaScript with comprehensive debugging
                    script_code = """
                        (function() {
                            console.log('[JS DEBUG] Starting script retrieval...');
                            const debugInfo = {
                                windowExists: typeof window !== 'undefined',
                                builderScriptsExists: typeof window.builderScripts !== 'undefined',
                                isArray: Array.isArray(window.builderScripts),
                                length: window.builderScripts ? window.builderScripts.length : 0,
                                sample: null
                            };
                            
                            if (window.builderScripts && window.builderScripts.length > 0) {
                                debugInfo.sample = {
                                    name: window.builderScripts[0].name,
                                    hasContent: !!window.builderScripts[0].content,
                                    contentLength: window.builderScripts[0].content ? window.builderScripts[0].content.length : 0,
                                    enabled: window.builderScripts[0].enabled,
                                    keys: Object.keys(window.builderScripts[0])
                                };
                                console.log('[JS DEBUG] Sample script:', debugInfo.sample);
                            }
                            
                            console.log('[JS DEBUG] Debug info:', debugInfo);
                            
                            try {
                                if (typeof window.builderScripts === 'undefined') {
                                    console.warn('[JS DEBUG] window.builderScripts is undefined');
                                    return JSON.stringify({error: 'undefined', debug: debugInfo, scripts: []});
                                }
                                
                                if (!Array.isArray(window.builderScripts)) {
                                    console.warn('[JS DEBUG] window.builderScripts is not an array:', typeof window.builderScripts);
                                    return JSON.stringify({error: 'not_array', debug: debugInfo, scripts: []});
                                }
                                
                                console.log('[JS DEBUG] window.builderScripts is array with length:', window.builderScripts.length);
                                
                                // Ensure all scripts have required fields
                                const validScripts = window.builderScripts.filter(s => {
                                    const valid = s && s.name && s.content;
                                    if (!valid) {
                                        console.warn('[JS DEBUG] Invalid script found:', s ? Object.keys(s) : 'null');
                                    }
                                    return valid;
                                });
                                
                                console.log('[JS DEBUG] Valid scripts count:', validScripts.length);
                                
                                const result = {
                                    success: true,
                                    debug: debugInfo,
                                    scripts: validScripts
                                };
                                
                                const jsonResult = JSON.stringify(result);
                                console.log('[JS DEBUG] Returning JSON length:', jsonResult.length);
                                return jsonResult;
                            } catch (e) {
                                console.error('[JS DEBUG] Error getting scripts:', e);
                                return JSON.stringify({error: String(e), debug: debugInfo, scripts: []});
                            }
                        })();
                    """
                    
                    DEBUG_LOGGER.info("BUILD: Executing JavaScript to retrieve scripts...")
                    self.web_view.page().runJavaScript(script_code, set_scripts_result)
                    
                    # Wait for result with timeout - INCREASED for large scripts
                    # Large scripts can take several seconds to serialize
                    DEBUG_LOGGER.info("BUILD: Waiting for JavaScript callback (allowing up to 10 seconds for large scripts)...")
                    max_attempts = 1000  # 10 seconds total (1000 * 0.01)
                    callback_received = False
                    for attempt in range(max_attempts):
                        if scripts_result[0] is not None:
                            callback_received = True
                            DEBUG_LOGGER.info(f"BUILD: Callback received after {attempt + 1} attempts ({attempt * 0.01:.2f} seconds)")
                            break
                        time.sleep(0.01)
                        # Log progress every 100 attempts (1 second)
                        if attempt > 0 and attempt % 100 == 0:
                            DEBUG_LOGGER.info(f"BUILD: Still waiting... ({attempt * 0.01:.1f} seconds elapsed)")
                    
                    if not callback_received:
                        DEBUG_LOGGER.warning(f"BUILD: Callback not received after {max_attempts} attempts ({max_attempts * 0.01:.1f} seconds)")
                        DEBUG_LOGGER.warning(f"BUILD: Callback was called: {callback_called[0]}")
                    
                    if scripts_result[0] is not None:
                        DEBUG_LOGGER.info(f"BUILD: Received result from JavaScript: {type(scripts_result[0])}")
                        DEBUG_LOGGER.info(f"BUILD: Result content length: {len(str(scripts_result[0]))}")
                        
                        try:
                            result_obj = json.loads(scripts_result[0])
                            DEBUG_LOGGER.info(f"BUILD: Parsed JSON successfully. Keys: {list(result_obj.keys())}")
                            
                            # Check if it's the new format with debug info
                            if 'scripts' in result_obj:
                                scripts_data = result_obj['scripts']
                                DEBUG_LOGGER.info(f"BUILD: Found {len(scripts_data)} script(s) in result.scripts")
                                
                                # Log debug info if available
                                if 'debug' in result_obj:
                                    debug_info = result_obj['debug']
                                    DEBUG_LOGGER.info(f"BUILD: JavaScript Debug Info:")
                                    DEBUG_LOGGER.info(f"  - window.builderScripts exists: {debug_info.get('builderScriptsExists', False)}")
                                    DEBUG_LOGGER.info(f"  - Is array: {debug_info.get('isArray', False)}")
                                    DEBUG_LOGGER.info(f"  - Length: {debug_info.get('length', 0)}")
                                    if debug_info.get('sample'):
                                        DEBUG_LOGGER.info(f"  - Sample script name: {debug_info['sample'].get('name', 'N/A')}")
                                        DEBUG_LOGGER.info(f"  - Sample has content: {debug_info['sample'].get('hasContent', False)}")
                                        DEBUG_LOGGER.info(f"  - Sample content length: {debug_info['sample'].get('contentLength', 0)}")
                                        DEBUG_LOGGER.info(f"  - Sample keys: {debug_info['sample'].get('keys', [])}")
                                
                                if 'error' in result_obj:
                                    DEBUG_LOGGER.error(f"BUILD: JavaScript reported error: {result_obj['error']}")
                            else:
                                # Old format - direct array
                                scripts_data = result_obj if isinstance(result_obj, list) else []
                                DEBUG_LOGGER.info(f"BUILD: Using old format, found {len(scripts_data)} script(s)")
                            
                            # Log details of each script
                            for i, script in enumerate(scripts_data):
                                DEBUG_LOGGER.info(f"BUILD: Script {i+1}: name='{script.get('name', 'N/A')}', "
                                                f"has_content={bool(script.get('content'))}, "
                                                f"content_len={len(script.get('content', ''))}, "
                                                f"enabled={script.get('enabled', True)}, "
                                                f"priority={script.get('priority', 0)}")
                            
                            # Sync to Python storage for next time
                            if scripts_data:
                                DEBUG_LOGGER.info(f"BUILD: Syncing {len(scripts_data)} script(s) from JavaScript to Python storage")
                                self.builder_scripts = scripts_data.copy()
                                DEBUG_LOGGER.info(f"BUILD: Python storage now has {len(self.builder_scripts)} script(s)")
                        except json.JSONDecodeError as e:
                            DEBUG_LOGGER.error(f"BUILD: Failed to parse scripts JSON: {e}")
                            DEBUG_LOGGER.error(f"BUILD: JSON string (first 1000 chars): {str(scripts_result[0])[:1000]}")
                            scripts_data = []
                    else:
                        # Even if we timed out, check one more time after a short delay
                        # Sometimes the callback arrives just after the loop (especially with large scripts)
                        DEBUG_LOGGER.warning("BUILD: No result after timeout, checking one more time after 0.5s delay...")
                        time.sleep(0.5)  # Give it a bit more time
                        if scripts_result[0] is not None:
                            DEBUG_LOGGER.info("BUILD: ✓ Result received after additional wait!")
                            try:
                                result_obj = json.loads(scripts_result[0])
                                if 'scripts' in result_obj:
                                    scripts_data = result_obj['scripts']
                                else:
                                    scripts_data = result_obj if isinstance(result_obj, list) else []
                                DEBUG_LOGGER.info(f"BUILD: Successfully retrieved {len(scripts_data)} script(s) after additional wait")
                            except Exception as e:
                                DEBUG_LOGGER.error(f"BUILD: Failed to parse result after additional wait: {e}")
                                scripts_data = []
                        else:
                            DEBUG_LOGGER.warning(f"BUILD: Still no result. Callback was called: {callback_called[0]}")
                            scripts_data = []
                except Exception as e:
                    DEBUG_LOGGER.error(f"BUILD: Exception retrieving scripts from JavaScript: {e}", exc_info=True)
                    scripts_data = []
                
                # If we successfully retrieved scripts from JavaScript, sync to Python storage
                if scripts_data:
                    DEBUG_LOGGER.info(f"BUILD: Syncing {len(scripts_data)} script(s) from JavaScript to Python storage")
                    self.builder_scripts = scripts_data.copy()
                    DEBUG_LOGGER.info(f"BUILD: Python storage now has {len(self.builder_scripts)} script(s)")
            
            DEBUG_LOGGER.info(f"BUILD: Final scripts_data count: {len(scripts_data)}")
            if scripts_data:
                DEBUG_LOGGER.info("BUILD: Scripts successfully retrieved:")
                for i, script in enumerate(scripts_data):
                    DEBUG_LOGGER.info(f"  [{i+1}] {script.get('name', 'N/A')} - "
                                    f"content: {len(script.get('content', ''))} chars, "
                                    f"enabled: {script.get('enabled', True)}")
            else:
                DEBUG_LOGGER.error("BUILD: CRITICAL - No scripts retrieved from JavaScript!")
                DEBUG_LOGGER.error("BUILD: This means window.builderScripts is empty or not accessible")
            DEBUG_LOGGER.info("=" * 80)
            
            # CRITICAL: Validate scripts_data before proceeding
            if not scripts_data:
                DEBUG_LOGGER.warning("No scripts found in window.builderScripts - user may need to load scripts first")
                self._append_terminal(f"⚠️ Warning: No scripts loaded. Please click '📁 Load Scripts' to load Python script files before building.\n", "warning")
                
                # Try to get debug info from JavaScript to help diagnose
                debug_info = [None]
                def set_debug_info(result):
                    debug_info[0] = result
                self.web_view.page().runJavaScript("""
                    JSON.stringify({
                        exists: typeof window.builderScripts !== 'undefined',
                        isArray: Array.isArray(window.builderScripts),
                        length: window.builderScripts ? window.builderScripts.length : 0,
                        sample: window.builderScripts && window.builderScripts.length > 0 ? 
                            {name: window.builderScripts[0].name, hasContent: !!window.builderScripts[0].content} : null
                    })
                """, set_debug_info)
                time.sleep(0.1)
                
                if debug_info[0]:
                    try:
                        debug_data = json.loads(debug_info[0])
                        DEBUG_LOGGER.info(f"JavaScript debug info: {debug_data}")
                        if debug_data.get('length', 0) > 0:
                            DEBUG_LOGGER.warning(f"Scripts exist in JS ({debug_data['length']}) but weren't retrieved - possible communication issue")
                    except:
                        pass
                
                QMessageBox.warning(
                    self, "No Scripts",
                    "No scripts found to build!\n\n"
                    "Please click '📁 Load Scripts' to select and load Python script files, then try building again.\n\n"
                    "Scripts must be loaded before building the 4DLLM file."
                )
                return
            
            DEBUG_LOGGER.info(f"Found {len(scripts_data)} script(s) to process for building")
            
            # Add scripts with auto-repair (using embedded builder)
            ScriptItem = builder_module.ScriptItem
            # Use the module-level function directly (not through builder_module instance)
            # The function is defined at module level, so we can reference it directly
            # No need to import - it's already in scope
            
            enabled_count = 0
            failed_count = 0
            for script in scripts_data:
                content = script.get('content', '')
                script_name = script.get('name', 'script')
                
                # Validate script name
                if not script_name or not script_name.strip():
                    DEBUG_LOGGER.warning(f"Skipping script with empty name")
                    failed_count += 1
                    continue
                
                script_name = script_name.strip()
                
                # Validate script content
                if not content or not content.strip():
                    DEBUG_LOGGER.warning(f"Script {script_name} has no content - skipping")
                    self._append_terminal(f"⚠️ Warning: Script '{script_name}' has no content - skipping\n", "warning")
                    failed_count += 1
                    continue
                
                script_enabled = script.get('enabled', True)  # Default to True if not specified
                
                # Ensure enabled is explicitly True (not just truthy)
                if script_enabled is False or script_enabled == 'false' or script_enabled == 0:
                    script_enabled = False
                else:
                    script_enabled = True
                
                # Auto-repair script if validation function is available
                if validate_and_fix_script:
                    try:
                        fixed_content, warnings = validate_and_fix_script(content, script_name)
                        if warnings:
                            self._append_terminal(f"Auto-repaired {script_name}: {', '.join(warnings[:3])}\n", "info")
                        content = fixed_content
                    except Exception as e:
                        DEBUG_LOGGER.warning(f"Failed to validate/fix {script_name}: {e}")
                
                # Parse imports if available in script data, otherwise parse from content
                imports = script.get('imports', [])
                if not imports:
                    # Parse imports from content using embedded function
                    # Use module-level function directly (not through builder_module instance)
                    try:
                        imports = parse_imports_from_script(content)
                    except Exception:
                        imports = []
                
                # Create ScriptItem
                try:
                    script_item = ScriptItem(
                        name=script_name,
                        content=content,
                        priority=script.get('priority', 0),
                        enabled=script_enabled,  # Use explicitly set enabled status
                        source_path=script.get('source_path', ''),
                        description=script.get('description', '')
                    )
                    # Set imports if ScriptItem supports it
                    if hasattr(script_item, 'imports'):
                        script_item.imports = imports
                    builder.add_script(script_item)
                    if script_enabled:
                        enabled_count += 1
                    DEBUG_LOGGER.info(f"Added script {script_name}: enabled={script_enabled}, priority={script_item.priority}")
                    self._append_terminal(f"✓ Added script: {script_name} (enabled={script_enabled}, priority={script_item.priority})\n", "info")
                except Exception as e:
                    DEBUG_LOGGER.error(f"Failed to create ScriptItem for {script_name}: {e}", exc_info=True)
                    self._append_terminal(f"✗ Failed to add script '{script_name}': {e}\n", "error")
                    failed_count += 1
                    continue
            
            # Final validation
            if enabled_count == 0:
                error_msg = f"No enabled scripts to build! ({len(scripts_data)} total, {failed_count} failed)"
                DEBUG_LOGGER.error(error_msg)
                self._append_terminal(f"✗ {error_msg}\n", "error")
                self._append_terminal(f"⚠️ Please enable at least one script before building.\n", "warning")
                QMessageBox.warning(
                    self, "No Enabled Scripts",
                    f"No enabled scripts found!\n\n"
                    f"Total scripts: {len(scripts_data)}\n"
                    f"Failed to process: {failed_count}\n"
                    f"Enabled scripts: 0\n\n"
                    f"Please enable at least one script using the toggle button (✓) before building."
                )
                return
            
            self._append_terminal(f"✓ Prepared {enabled_count} enabled script(s) for building\n", "success")
            if failed_count > 0:
                self._append_terminal(f"⚠️ {failed_count} script(s) failed to process\n", "warning")
            
            # Validate and auto-fix scripts before building (like standalone builder)
            validation_warnings = []
            fixed_count = 0
            
            # validate_and_fix_script is already defined at module level, so it's in scope
            # No need to assign from builder_module - just use it directly
            
            for script_item in builder.scripts:
                if not script_item.enabled:
                    continue
                # Validate and auto-fix
                try:
                    fixed_content, warnings = validate_and_fix_script(script_item.content, script_item.name)
                    if warnings:
                        validation_warnings.append((script_item.name, warnings))
                        script_item.content = fixed_content
                        # Use module-level function directly (not through builder_module instance)
                        script_item.imports = parse_imports_from_script(fixed_content)
                        fixed_count += 1
                        BUILDER_DEBUG.log_event("pre_build_script_fixed", {
                            "build_id": build_id,
                            "script_name": script_item.name,
                            "warnings": warnings[:10],
                            "content_length_before": len(script_item.content),
                            "content_length_after": len(fixed_content)
                        })
                    else:
                        BUILDER_DEBUG.log_event("pre_build_script_validated", {
                            "build_id": build_id,
                            "script_name": script_item.name,
                            "no_warnings": True
                        })
                except Exception as e:
                    import traceback
                    BUILDER_DEBUG.log_error("pre_build_validation_error", {
                        "build_id": build_id,
                        "script_name": script_item.name,
                        "error": str(e)
                    }, traceback.format_exc())
                    raise
                # Final syntax check
                try:
                    compile(script_item.content, script_item.name, "exec")
                except SyntaxError as se:
                    validation_warnings.append((script_item.name, [
                        f"Syntax error at line {se.lineno}: {se.msg}",
                        "Could not auto-fix. Please review manually."
                    ]))
                except Exception as e:
                    validation_warnings.append((script_item.name, [f"Error: {str(e)}"]))
            
            # Show validation results (like standalone builder)
            if validation_warnings:
                warning_text = []
                error_count = 0
                fix_count = 0
                for script_name, warnings in validation_warnings:
                    warning_text.append(f"\n{script_name}:")
                    for warning in warnings:
                        warning_text.append(f"  • {warning}")
                        if "Fixed:" in warning:
                            fix_count += 1
                        elif "error" in warning.lower() or "Error" in warning:
                            error_count += 1
                if error_count > 0:
                    msg = f"Found {error_count} error(s) and fixed {fix_count} issue(s):\n" + "\n".join(warning_text)
                    result = QMessageBox.question(
                        self, "Validation Results",
                        msg + "\n\nSome errors could not be auto-fixed.\nContinue building anyway?",
                        QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
                    )
                    if result != QMessageBox.StandardButton.Yes:
                        return
                elif fix_count > 0:
                    summary = "\n".join(warning_text[:15])
                    if len(warning_text) > 15:
                        summary += f"\n  ... and {len(warning_text) - 15} more fix(es)"
                    QMessageBox.information(
                        self, "✅ Auto-Fixes Applied",
                        f"Fixed {fix_count} issue(s) in your scripts:\n{summary}"
                    )
            
            # Build
            result_path = builder.build(output_path, compress_scripts, compress_metadata)
            file_size = Path(result_path).stat().st_size / (1024 * 1024)
            
            # Post-build validation (like standalone builder)
            try:
                # Use module-level function directly (not through builder_module instance)
                validation = validate_built_file(Path(result_path))
                summary = (
                    f"Sections: {validation.get('section_count', '?')}, "
                    f"Scripts: {validation.get('script_sections', '?')}, "
                    f"TOC @ {validation.get('toc_offset', '?')}"
                )
                self._append_terminal(f"✓ Build successful!\n", "success")
                self._append_terminal(f"File: {Path(result_path).name}\n", "info")
                self._append_terminal(f"Size: {file_size:.2f} MB\n", "info")
                self._append_terminal(f"Validation: {summary}\n", "info")
                
                QMessageBox.information(
                    self, "Success",
                    f"4DLLM created and validated!\n\n"
                    f"File: {Path(result_path).name}\n"
                    f"Size: {file_size:.2f} MB\n"
                    f"Scripts: {enabled_count}\n"
                    f"{summary}"
                )
            except Exception as e:
                self._append_terminal(f"✓ Build successful but validation failed: {e}\n", "warning")
                QMessageBox.warning(
                    self, "Build Complete (Validation Warning)",
                    f"4DLLM file created but validation failed:\n{e}\n\n"
                    f"File: {Path(result_path).name}\n"
                    f"Size: {file_size:.2f} MB"
                )
        except Exception as e:
            error_msg = f"Build failed: {str(e)}"
            DEBUG_LOGGER.error(error_msg, exc_info=True)
            self._append_terminal(f"✗ {error_msg}\n", "error")
            QMessageBox.critical(self, "Build Failed", error_msg)
    
    def _load_modules(self):
        """Load scripts from user-selected files - USER SELECTION TAKES PRIORITY"""
        # ========== VERBOSE DEBUG LOGGING ==========
        load_id = f"load_{int(time.time() * 1000)}"
        BUILDER_DEBUG.log_event("load_modules_called", {
            "load_id": load_id,
            "python_storage_before": len(self.builder_scripts),
            "python_storage_scripts": [s.get("name", "N/A") for s in self.builder_scripts]
        })
        BUILDER_DEBUG.snapshot_python_storage(self.builder_scripts, f"before_load_{load_id}")
        
        # User explicitly clicked "Load Scripts" - they want to select files
        # UNIVERSAL: Accept any file type, not just Python
        file_paths, _ = QFileDialog.getOpenFileNames(
            self, 
            "Select Script Files to Load (Any Type)", 
            "", 
            "All Files (*);;Python Files (*.py);;JavaScript Files (*.js);;Text Files (*.txt);;Shell Scripts (*.sh);;Batch Files (*.bat)"
        )
        
        BUILDER_DEBUG.log_event("load_modules_file_dialog", {
            "load_id": load_id,
            "files_selected": len(file_paths) if file_paths else 0,
            "file_paths": [str(p) for p in file_paths] if file_paths else []
        })
        
        # If user cancelled, do nothing
        if not file_paths:
            BUILDER_DEBUG.log_event("load_modules_cancelled", {
                "load_id": load_id
            })
            return
        
        # User selected files - load ONLY those (replace existing)
        scripts_loaded = []
        total_modules = 0
        all_warnings = []
        
        ScriptItem = builder_module.ScriptItem
        # validate_and_fix_script is already defined at module level, so it's in scope
        # No need to assign from builder_module - just use it directly
        
        for script_file_path in file_paths:
                script_file = Path(script_file_path)
                try:
                    if not script_file.exists():
                        DEBUG_LOGGER.warning(f"File not found: {script_file}")
                        continue
                    
                    # UNIVERSAL: Try to read as text (any file type)
                    try:
                        original_content = script_file.read_text(encoding='utf-8')
                    except UnicodeDecodeError:
                        # File is not UTF-8 text, skip it
                        DEBUG_LOGGER.warning(f"File {script_file.name} is not UTF-8 text, skipping")
                        all_warnings.append(f"{script_file.name}: Not a UTF-8 text file")
                        continue
                    
                    fixed_content = original_content
                    script_warnings = []
                    
                    # Only validate/fix if it's a Python file (has .py extension)
                    if validate_and_fix_script and script_file.suffix.lower() == '.py':
                        try:
                            fixed_content, script_warnings = validate_and_fix_script(original_content, script_file.name)
                            if script_warnings:
                                all_warnings.extend([f"{script_file.name}: {w}" for w in script_warnings])
                                DEBUG_LOGGER.info(f"Script {script_file.name} auto-repaired: {script_warnings}")
                        except Exception as e:
                            DEBUG_LOGGER.warning(f"Failed to validate/fix {script_file.name}: {e}")
                    
                    # Only parse imports for Python files
                    imports = []
                    if script_file.suffix.lower() == '.py':
                        try:
                            imports = parse_imports_from_script(fixed_content)
                        except Exception:
                            pass
                    
                    try:
                        script_item = ScriptItem(
                            name=script_file.name,
                            content=fixed_content,
                            priority=0,
                            enabled=True,
                            source_path=str(script_file),
                            description=f"Loaded from {script_file.name}"
                        )
                        script_item.imports = imports
                        total_modules += len(imports)
                        scripts_loaded.append({
                            'name': script_item.name,
                            'content': script_item.content,
                            'priority': script_item.priority,
                            'enabled': script_item.enabled,
                            'source_path': script_item.source_path,
                            'description': script_item.description,
                            'imports': script_item.imports
                        })
                    except Exception as e:
                        DEBUG_LOGGER.error(f"Failed to create ScriptItem for {script_file.name}: {e}")
                        scripts_loaded.append({
                            'name': script_file.name,
                            'content': fixed_content,
                            'priority': 0,
                            'enabled': True,
                            'source_path': str(script_file),
                            'description': f"Loaded from {script_file.name}",
                            'imports': imports
                        })
                        total_modules += len(imports)
                except Exception as e:
                    DEBUG_LOGGER.warning(f"Failed to load {script_file}: {e}", exc_info=True)
                    all_warnings.append(f"{script_file.name}: Failed to load - {str(e)}")
        
        # Store scripts in Python (like standalone builder) - avoids JavaScript async issues
        if scripts_loaded:
            BUILDER_DEBUG.log_script_load(load_id, {
                "action": "load_scripts",
                "source": "user_selection",
                "file_count": len(file_paths),
                "scripts_loaded_count": len(scripts_loaded),
                "file_paths": [str(p) for p in file_paths],
                "python_storage_before": len(self.builder_scripts)
            })
            
            DEBUG_LOGGER.info("=" * 80)
            DEBUG_LOGGER.info("LOAD: Storing scripts in Python (like standalone builder)")
            DEBUG_LOGGER.info(f"LOAD: Storing {len(scripts_loaded)} script(s) in self.builder_scripts")
            
            # Snapshot BEFORE storing
            BUILDER_DEBUG.snapshot_python_storage(self.builder_scripts, f"before_load_{load_id}")
            
            # Store in Python - this is the source of truth
            self.builder_scripts = scripts_loaded.copy()
            
            # Snapshot AFTER storing
            BUILDER_DEBUG.snapshot_python_storage(self.builder_scripts, f"after_load_{load_id}")
            
            # Log each script being stored
            for i, script in enumerate(scripts_loaded):
                script_info = {
                    "index": i + 1,
                    "name": script.get('name', 'N/A'),
                    "content_len": len(script.get('content', '')),
                    "enabled": script.get('enabled', True),
                    "priority": script.get('priority', 0),
                    "imports_count": len(script.get('imports', [])),
                    "source_path": script.get('source_path', 'N/A')
                }
                DEBUG_LOGGER.info(f"LOAD: Script {i+1}: name='{script_info['name']}', "
                                f"content_len={script_info['content_len']}, "
                                f"enabled={script_info['enabled']}, "
                                f"priority={script_info['priority']}, "
                                f"imports={script_info['imports_count']}")
                BUILDER_DEBUG.log_event("script_stored", {
                    "load_id": load_id,
                    "script": script_info
                })
            
            # Also update JavaScript for UI display
            scripts_json = json.dumps(scripts_loaded)
            scripts_json_escaped = scripts_json.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '\\r')
            self._execute_js(f"""
                try {{
                    const scriptsJson = '{scripts_json_escaped}';
                    window.builderScripts = JSON.parse(scriptsJson);
                    if (typeof updateScriptsList === 'function') {{
                        updateScriptsList();
                    }}
                }} catch (e) {{
                    console.error('[JS LOAD] Error:', e);
                }}
            """)
            
            DEBUG_LOGGER.info("LOAD: Scripts stored in both Python and JavaScript")
            DEBUG_LOGGER.info("=" * 80)
            
            BUILDER_DEBUG.log_event("load_modules_complete", {
                "load_id": load_id,
                "scripts_stored": len(scripts_loaded),
                "python_storage_after": len(self.builder_scripts),
                "total_modules": total_modules
            })
            
            # Show success message
            mod_info = f" ({total_modules} imports)" if total_modules > 0 else ""
            message = f"✓ Loaded {len(scripts_loaded)} script(s) from {len(file_paths)} file(s){mod_info}"
            
            if all_warnings:
                warning_text = "\n".join(all_warnings[:10])
                if len(all_warnings) > 10:
                    warning_text += f"\n... and {len(all_warnings) - 10} more warnings"
                message += f"\n\nAuto-repair warnings:\n{warning_text}"
                QMessageBox.information(self, "Scripts Loaded (with repairs)", message)
            else:
                QMessageBox.information(self, "Scripts Loaded", message)
            
            DEBUG_LOGGER.info(f"User loaded {len(scripts_loaded)} script(s) from {len(file_paths)} file(s)")
        else:
            # No scripts were successfully loaded
            QMessageBox.warning(
                self, "No Scripts Loaded",
                f"Failed to load any scripts from the selected files.\n\n"
                f"Please check:\n"
                f"1. Files are readable\n"
                f"2. Files exist and are accessible\n"
                f"3. Files are not empty"
            )
    
    def _load_modules_folder(self):
        """Load scripts from ./modules/ folder (like standalone builder) - UNIVERSAL: any file type"""
        if not self.modules_dir.exists():
            QMessageBox.showwarning(
                self, "modules folder missing",
                f"Expected folder:\n{self.modules_dir}\n\nCreate it and drop script files inside (any type)."
            )
            return
        
        # UNIVERSAL: Load any file type, not just .py
        all_files = sorted([p for p in self.modules_dir.glob("*") if p.is_file() and not p.name.startswith('.')])
        if not all_files:
            QMessageBox.showinfo(self, "No modules", f"No files found in:\n{self.modules_dir}")
            return
        
        scripts_loaded = []
        total_modules = 0
        
        ScriptItem = builder_module.ScriptItem
        
        for p in all_files:
            try:
                # Try UTF-8 first, fallback for binary files
                try:
                    content = p.read_text(encoding="utf-8")
                except UnicodeDecodeError:
                    DEBUG_LOGGER.warning(f"File {p.name} is not UTF-8 text, skipping")
                    continue
            except Exception:
                continue
            
            item = ScriptItem(
                name=p.name,
                content=content,
                description="Module folder script",
                priority=7,  # Same as standalone builder
                enabled=True,
                source_path=str(p),
            )
            # Only parse imports for Python files
            if p.suffix.lower() == '.py':
                item.imports = parse_imports_from_script(item.content)
            else:
                item.imports = []
            total_modules += len(item.imports)
            
            # Convert to dict for JavaScript
            scripts_loaded.append({
                'name': item.name,
                'content': item.content,
                'priority': item.priority,
                'enabled': item.enabled,
                'source_path': item.source_path,
                'description': item.description,
                'imports': item.imports
            })
        
        if scripts_loaded:
            DEBUG_LOGGER.info("=" * 80)
            DEBUG_LOGGER.info("LOAD_MODULES_FOLDER: Starting script injection into JavaScript")
            DEBUG_LOGGER.info(f"LOAD_MODULES_FOLDER: Preparing {len(scripts_loaded)} script(s) for injection")
            
            scripts_json = json.dumps(scripts_loaded)
            DEBUG_LOGGER.info(f"LOAD_MODULES_FOLDER: JSON string length: {len(scripts_json)}")
            
            scripts_json_escaped = scripts_json.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '\\r')
            DEBUG_LOGGER.info(f"LOAD_MODULES_FOLDER: Escaped JSON string length: {len(scripts_json_escaped)}")
            
            self._execute_js(f"""
                try {{
                    console.log('[JS LOAD_MODULES] Starting script injection...');
                    if (!window.builderScripts) {{
                        console.log('[JS LOAD_MODULES] Initializing window.builderScripts array');
                        window.builderScripts = [];
                    }}
                    const scriptsJson = '{scripts_json_escaped}';
                    console.log('[JS LOAD_MODULES] JSON string length:', scriptsJson.length);
                    const newScripts = JSON.parse(scriptsJson);
                    console.log('[JS LOAD_MODULES] Parsed successfully, count:', newScripts.length);
                    newScripts.forEach((newScript, idx) => {{
                        const existingIndex = window.builderScripts.findIndex(s => s.name === newScript.name);
                        if (existingIndex >= 0) {{
                            console.log('[JS LOAD_MODULES] Replacing existing script:', newScript.name);
                            window.builderScripts[existingIndex] = newScript;
                        }} else {{
                            console.log('[JS LOAD_MODULES] Adding new script:', newScript.name);
                            window.builderScripts.push(newScript);
                        }}
                    }});
                    console.log('[JS LOAD_MODULES] Final window.builderScripts.length:', window.builderScripts.length);
                    if (typeof updateScriptsList === 'function') {{
                        updateScriptsList();
                    }} else {{
                        console.warn('[JS LOAD_MODULES] updateScriptsList function not found!');
                    }}
                }} catch (e) {{
                    console.error('[JS LOAD_MODULES] Error loading scripts:', e);
                }}
            """)
            
            DEBUG_LOGGER.info("LOAD_MODULES_FOLDER: Script injection completed")
            DEBUG_LOGGER.info("=" * 80)
            mod_info = f" ({total_modules} total modules)" if total_modules > 0 else ""
            QMessageBox.information(self, "Success", f"Loaded {len(scripts_loaded)} module(s) from ./{MODULES_DIRNAME}/{mod_info}")
    
    def _auto_load_modules_folder(self):
        """Auto-load modules folder on startup (like standalone builder) - UNIVERSAL: any file type"""
        if self.modules_dir.exists() and self.modules_dir.is_dir():
            # UNIVERSAL: Load any file type, not just .py
            all_files = sorted([p for p in self.modules_dir.glob("*") if p.is_file() and not p.name.startswith('.')])
            if all_files:
                # Load silently (no dialog)
                scripts_loaded = []
                ScriptItem = builder_module.ScriptItem
                
                for p in all_files:
                    try:
                        # Try UTF-8 first, fallback for binary files
                        try:
                            content = p.read_text(encoding="utf-8")
                        except UnicodeDecodeError:
                            DEBUG_LOGGER.warning(f"File {p.name} is not UTF-8 text, skipping")
                            continue
                        item = ScriptItem(
                            name=p.name,
                            content=content,
                            description="Module folder script",
                            priority=7,
                            enabled=True,
                            source_path=str(p),
                        )
                        # Only parse imports for Python files
                        if p.suffix.lower() == '.py':
                            item.imports = parse_imports_from_script(item.content)
                        else:
                            item.imports = []
                        scripts_loaded.append({
                            'name': item.name,
                            'content': item.content,
                            'priority': item.priority,
                            'enabled': item.enabled,
                            'source_path': item.source_path,
                            'description': item.description,
                            'imports': item.imports
                        })
                    except Exception:
                        continue
                
                if scripts_loaded:
                    # ========== CRITICAL FIX: Store in Python storage FIRST ==========
                    DEBUG_LOGGER.info("=" * 80)
                    DEBUG_LOGGER.info("AUTO_LOAD: Storing scripts in Python (like standalone builder)")
                    DEBUG_LOGGER.info(f"AUTO_LOAD: Storing {len(scripts_loaded)} script(s) in self.builder_scripts")
                    
                    # Store in Python - this is the source of truth
                    for new_script in scripts_loaded:
                        existing_index = next((i for i, s in enumerate(self.builder_scripts) if s.get('name') == new_script.get('name')), None)
                        if existing_index is not None:
                            self.builder_scripts[existing_index] = new_script
                        else:
                            self.builder_scripts.append(new_script)
                    
                    auto_load_id = f"auto_load_{int(time.time() * 1000)}"
                    BUILDER_DEBUG.log_script_load(auto_load_id, {
                        "action": "auto_load_modules_folder",
                        "source": "modules_folder",
                        "scripts_loaded_count": len(scripts_loaded),
                        "python_storage_after": len(self.builder_scripts),
                        "scripts": [{"name": s.get("name"), "enabled": s.get("enabled")} for s in scripts_loaded]
                    })
                    BUILDER_DEBUG.snapshot_python_storage(self.builder_scripts, "after_auto_load")
                    
                    DEBUG_LOGGER.info(f"AUTO_LOAD: Stored {len(self.builder_scripts)} total script(s) in Python")
                    DEBUG_LOGGER.info("=" * 80)
                    
                    # Also update JavaScript for UI display - use Python storage as source
                    scripts_json = json.dumps(self.builder_scripts)
                    scripts_json_escaped = scripts_json.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '\\r')
                    self._execute_js(f"""
                        try {{
                            if (!window.builderScripts) {{
                                window.builderScripts = [];
                            }}
                            const scriptsJson = '{scripts_json_escaped}';
                            window.builderScripts = JSON.parse(scriptsJson);
                            if (typeof updateScriptsList === 'function') {{
                                updateScriptsList();
                            }}
                        }} catch (e) {{
                            console.error('[JS AUTO_LOAD] Error:', e);
                        }}
                    """)
                    DEBUG_LOGGER.info(f"AUTO_LOAD: Auto-loaded {len(scripts_loaded)} module(s) from ./{MODULES_DIRNAME}/")
    
    def _add_script(self, name: str, content: str, priority: int, enabled: bool):
        """Add a script to builder"""
        script_data = {
            'name': name,
            'content': content,
            'priority': priority,
            'enabled': enabled,
            'source_path': '',
            'description': ''
        }
        scripts_json = json.dumps([script_data])
        scripts_json_escaped = scripts_json.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '\\r')
        self._execute_js(f"""
            try {{
                if (!window.builderScripts) {{
                    window.builderScripts = [];
                }}
                const scriptsJson = '{scripts_json_escaped}';
                const newScript = JSON.parse(scriptsJson)[0];
                window.builderScripts.push(newScript);
                if (typeof updateScriptsList === 'function') {{
                    updateScriptsList();
                }}
            }} catch (e) {{
                console.error('Error adding script:', e);
            }}
        """)
    
    def _select_build_output_path(self):
        """Open file dialog to select output path for build"""
        output_path, _ = QFileDialog.getSaveFileName(
            self, "Save 4DLLM File", "", "4DLLM Files (*.4dllm);;All Files (*)"
        )
        if output_path:
            # Update the input field in the UI
            escaped_path = output_path.replace('\\', '\\\\').replace("'", "\\'")
            self._execute_js(f"document.getElementById('build-output-path').value = '{escaped_path}';")

    def _save_builder_system_prompt(self, text: str):
        """Persist builder system prompt to disk (source of truth for save button)."""
        try:
            payload = {
                "system_prompt": text or "",
                "saved_at": datetime.utcnow().isoformat() + "Z",
            }
            self.builder_prompt_file.write_text(json.dumps(payload, indent=2), encoding="utf-8")
            DEBUG_LOGGER.info(f"BUILD: system prompt saved to file: {self.builder_prompt_file} (len={len(text or '')})")
            escaped = (text or "").replace("\\", "\\\\").replace("'", "\\'")
            self._execute_js(
                "try { "
                f"localStorage.setItem('builder_system_prompt', '{escaped}');"
                "} catch(e) {}"
            )
            self._execute_js("document.getElementById('build-status').textContent = 'System prompt saved';")
        except Exception as e:
            DEBUG_LOGGER.error(f"BUILD: Failed to save system prompt to file: {e}", exc_info=True)

    def _load_builder_system_prompt(self):
        """Load builder system prompt from disk into the UI."""
        try:
            if self.builder_prompt_file.exists():
                data = json.loads(self.builder_prompt_file.read_text(encoding="utf-8"))
                text = data.get("system_prompt", "") if isinstance(data, dict) else ""
            else:
                text = ""
            escaped = (text or "").replace("\\", "\\\\").replace("'", "\\'")
            self._execute_js(
                "try { "
                f"document.getElementById('build-system-prompt').value = '{escaped}';"
                f"localStorage.setItem('builder_system_prompt', '{escaped}');"
                "} catch(e) {}"
            )
            DEBUG_LOGGER.info(f"BUILD: system prompt loaded from file (len={len(text or '')})")
        except Exception as e:
            DEBUG_LOGGER.error(f"BUILD: Failed to load system prompt from file: {e}", exc_info=True)

    def _clear_builder_system_prompt(self):
        """Clear builder system prompt from disk and UI."""
        try:
            if self.builder_prompt_file.exists():
                self.builder_prompt_file.unlink()
            self._execute_js(
                "try { "
                "document.getElementById('build-system-prompt').value = '';"
                "localStorage.removeItem('builder_system_prompt');"
                "} catch(e) {}"
            )
            DEBUG_LOGGER.info("BUILD: system prompt cleared from file and UI")
        except Exception as e:
            DEBUG_LOGGER.error(f"BUILD: Failed to clear system prompt: {e}", exc_info=True)
    
    def _remove_script(self, name: str):
        """Remove a script from builder"""
        # Remove from Python storage
        self.builder_scripts = [s for s in self.builder_scripts if s.get('name') != name]
        DEBUG_LOGGER.info(f"Removed script '{name}' from Python storage. Remaining: {len(self.builder_scripts)}")
        
        # Also update JavaScript for UI
        self._execute_js(f"""
            window.builderScripts = window.builderScripts.filter(s => s.name !== '{name.replace("'", "\\'")}');
            if (typeof updateScriptsList === 'function') {{ updateScriptsList(); }}
        """)
    
    def _load_editor_file(self):
        """Load a file for editing (.gguf or .4dllm) - runs in background thread to prevent freeze"""
        import threading
        EDITOR_LOGGER.info(f"_load_editor_file() called - Main thread: {threading.main_thread().ident}, Current thread: {threading.current_thread().ident}")
        try:
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Load File for Editing", "", 
                "All Supported (*.gguf *.4dllm);;GGUF Files (*.gguf);;4DLLM Files (*.4dllm);;All Files (*)"
            )
            EDITOR_LOGGER.info(f"File dialog returned: {file_path}")
            if not file_path:
                EDITOR_LOGGER.info("User cancelled file selection")
                return
            
            file_path_obj = Path(file_path)
            file_size_mb = file_path_obj.stat().st_size / (1024*1024)
            EDITOR_LOGGER.info(f"Selected file: {file_path_obj}, size: {file_size_mb:.2f} MB")
            
            EDITOR_LOGGER.info("Calling _execute_js from main thread (safe)")
            self._execute_js(f"document.getElementById('editor-file-info').textContent = 'Loading: {file_path_obj.name} ({file_size_mb:.2f} MB)...';")
            self._execute_js("document.getElementById('editor-content').innerHTML = '<div style=\"padding: 40px; text-align: center; color: #06b6d4;\"><div style=\"font-size: 18px; margin-bottom: 12px;\">⏳ Loading file...</div><div style=\"font-size: 13px;\">Please wait, this may take a moment for large files</div></div>';")
            EDITOR_LOGGER.info("JavaScript calls completed")
        except Exception as e:
            import traceback
            EDITOR_LOGGER.error(f"Exception in _load_editor_file (main thread): {e}", exc_info=True)
            DEBUG_LOGGER.error(f"Exception in _load_editor_file: {e}", exc_info=True)
            return
        
        # CRITICAL: Capture signal references and module references on main thread before entering background thread
        # This avoids accessing 'self' (Qt object) or global variables from background thread
        terminal_signal = self.terminalAppendRequested
        error_signal = self.showErrorDialogRequested
        metadata_signal = self.displayEditorMetadataRequested
        store_editor_signal = self.storeEditorInstanceRequested
        execute_js_safe_method = self._execute_js_safe
        
        # Capture module references on main thread
        runner_module_ref = runner_module
        editor_module_ref = editor_module
        
        # Load in background thread to prevent UI freeze
        def load_in_thread():
            import threading
            thread_id = threading.current_thread().ident
            thread_name = threading.current_thread().name
            
            # Force flush logger immediately - use try/except to catch any issues
            try:
                EDITOR_LOGGER.info(f"[THREAD] load_in_thread() STARTED - Thread ID: {thread_id}, Name: {thread_name}")
                EDITOR_LOGGER.info(f"[THREAD] File path: {file_path_obj}")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
            except Exception as log_err:
                # Even logging failed - write directly to file as last resort
                try:
                    with open(EDITOR_DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
                        f.write(f"[DIRECT_WRITE] Failed to log normally: {log_err}\n")
                        f.write(f"[DIRECT_WRITE] load_in_thread() STARTED - Thread: {thread_id}, File: {file_path_obj}\n")
                        f.flush()
                except:
                    pass
            
            try:
                # Use captured module references instead of global variables
                EDITOR_LOGGER.info("[THREAD] Using cached module references (runner_module, editor_module)")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
                
                # Detect file type and load accordingly
                suffix = str(file_path_obj.suffix).lower()
                EDITOR_LOGGER.info(f"[THREAD] File suffix: {suffix}")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
                
                if suffix == '.4dllm':
                    EDITOR_LOGGER.info("[THREAD] Detected 4DLLM file")
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                    
                    # Verify runner_module is available (using cached reference)
                    if runner_module_ref is None:
                        EDITOR_LOGGER.error("[THREAD] CRITICAL: runner_module_ref is None - cannot proceed")
                        for handler in EDITOR_LOGGER.handlers:
                            handler.flush()
                        error_msg = "Runner module not loaded - cannot process 4DLLM files.\n\nThe 4dllm_runner.py module failed to load during startup. Please check:\n1. That 4dllm_runner.py exists in the same directory\n2. That there are no syntax errors in 4dllm_runner.py\n3. Check the debug log for module loading errors"
                        error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-THREAD-001\n\nRunner module failed to load. This usually happens if:\n- 4dllm_runner.py is missing or has errors\n- Python 3.12 dataclass compatibility issue\nCheck the main debug log (4dllm_runner_debug.log) for details.")
                        execute_js_safe_method("document.getElementById('editor-file-info').textContent = 'Error: Runner module not available';")
                        return
                    
                    EDITOR_LOGGER.info("[THREAD] runner_module_ref verified, calling _load_4dllm_for_editing()")
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                    # Pass signal references and module references to avoid accessing globals from background thread
                    self._load_4dllm_for_editing(file_path_obj, terminal_signal, error_signal, metadata_signal, store_editor_signal, execute_js_safe_method, runner_module_ref, editor_module_ref)
                elif suffix == '.gguf':
                    EDITOR_LOGGER.info("[THREAD] Detected GGUF file, calling _load_gguf_for_editing()")
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                    # Pass module references for GGUF loading too
                    self._load_gguf_for_editing(
                        file_path_obj,
                        terminal_signal=terminal_signal,
                        error_signal=error_signal,
                        metadata_signal=metadata_signal,
                        store_editor_signal=None,
                        execute_js_method=execute_js_safe_method,
                        editor_module_ref=editor_module_ref,
                        runner_module_ref=runner_module_ref,
                    )
                else:
                    EDITOR_LOGGER.warning(f"[THREAD] Unsupported file type: {suffix}")
                    error_msg = "Unsupported file type!"
                    # Do NOT call Qt/QtWebEngine APIs from this worker thread
                    EDITOR_LOGGER.info("[THREAD] Emitting showErrorDialogRequested signal (thread-safe)")
                    error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-005")
                    EDITOR_LOGGER.info("[THREAD] Calling _execute_js_safe (thread-safe)")
                    execute_js_safe_method("document.getElementById('editor-file-info').textContent = 'Error: Unsupported file type';")
            except Exception as e:
                import traceback
                error_msg = f"Failed to load file: {e}"
                error_code = "ERR-EDITOR-006"
                traceback_str = traceback.format_exc()
                EDITOR_LOGGER.error(f"[THREAD] Exception in load_in_thread: {e}", exc_info=True)
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                # Do NOT call Qt/QtWebEngine APIs from this worker thread
                EDITOR_LOGGER.info("[THREAD] Emitting showErrorDialogRequested signal (thread-safe)")
                error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
                EDITOR_LOGGER.info("[THREAD] Calling _execute_js_safe (thread-safe)")
                execute_js_safe_method("document.getElementById('editor-file-info').textContent = 'Error loading file';")
        
        # Start loading in background thread
        import threading
        EDITOR_LOGGER.info("Creating background thread for file loading")
        thread = threading.Thread(target=load_in_thread, daemon=True, name="EditorFileLoader")
        thread.start()
        EDITOR_LOGGER.info(f"Background thread started: {thread.name}, ID: {thread.ident}")
    
    def _load_4dllm_for_editing(self, file_path: Path, terminal_signal=None, error_signal=None, metadata_signal=None, store_editor_signal=None, execute_js_method=None, runner_module_ref=None, editor_module_ref=None):
        """Load 4DLLM file for editing - runs in background thread"""
        import threading
        # Use provided signal references to avoid accessing 'self' from background thread
        if terminal_signal is None:
            terminal_signal = self.terminalAppendRequested
        if error_signal is None:
            error_signal = self.showErrorDialogRequested
        if metadata_signal is None:
            metadata_signal = self.displayEditorMetadataRequested
        if execute_js_method is None:
            execute_js_method = self._execute_js_safe
        # Use provided module references instead of global variables
        if runner_module_ref is None:
            runner_module_ref = runner_module
        if editor_module_ref is None:
            editor_module_ref = editor_module
            
        EDITOR_LOGGER.info("=" * 80)
        EDITOR_LOGGER.info(f"[THREAD] _load_4dllm_for_editing() called - Thread: {threading.current_thread().ident}, Name: {threading.current_thread().name}")
        EDITOR_LOGGER.info(f"[THREAD] File path: {file_path}")
        EDITOR_LOGGER.info(f"[THREAD] runner_module_ref is None: {runner_module_ref is None}")
        EDITOR_LOGGER.info(f"[THREAD] editor_module_ref is None: {editor_module_ref is None}")
        for handler in EDITOR_LOGGER.handlers:
            handler.flush()
        try:
            # Extract GGUF from 4DLLM for editing
            EDITOR_LOGGER.info(f"[THREAD] About to emit terminal signal (using cached reference)")
            terminal_signal.emit(f"Loading 4DLLM file: {file_path.name}\n", "info")
            EDITOR_LOGGER.info(f"[THREAD] terminal signal emitted successfully")
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()
            
            # Use runner utilities to extract GGUF (using cached module reference)
            EDITOR_LOGGER.info(f"[THREAD] Checking runner_module_ref: {runner_module_ref is not None}")
            if not runner_module_ref:
                error_msg = "Runner module not available - cannot load 4DLLM files. Please check that 4dllm_runner.py is accessible."
                EDITOR_LOGGER.error(f"[THREAD] {error_msg}")
                error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-4DLLM-000\n\nThe runner module failed to load. Check the debug log for details.")
                execute_js_method("document.getElementById('editor-file-info').textContent = 'Error: Runner module not available';")
                return
            
            if runner_module_ref:
                EDITOR_LOGGER.info("[THREAD] runner_module_ref is available, calling read_and_validate_toc()")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
                try:
                    entries, toc_offset = runner_module_ref.read_and_validate_toc(file_path)
                    EDITOR_LOGGER.info(f"[THREAD] read_and_validate_toc() returned {len(entries)} entries, toc_offset={toc_offset}")
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                except Exception as e:
                    import traceback
                    EDITOR_LOGGER.error(f"[THREAD] read_and_validate_toc() failed: {e}", exc_info=True)
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                    raise
                
                EDITOR_LOGGER.info(f"[THREAD] Searching for GGUF section (SECTION_GGUF_DATA={runner_module_ref.SECTION_GGUF_DATA})")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
                gguf_entry = next((e for e in entries if e.section_type == runner_module_ref.SECTION_GGUF_DATA), None)
                EDITOR_LOGGER.info(f"[THREAD] GGUF entry found: {gguf_entry is not None}")
                if gguf_entry:
                    terminal_signal.emit("Found GGUF section in 4DLLM file\n", "info")
                    
                    # Extract GGUF to temp file for editing
                    import tempfile
                    temp_gguf = tempfile.NamedTemporaryFile(delete=False, suffix='.gguf')
                    temp_gguf_path = Path(temp_gguf.name)
                    temp_gguf.close()
                    
                    # Read GGUF section (using cached module reference)
                    EDITOR_LOGGER.info("[THREAD] Reading GGUF section from file")
                    for handler in EDITOR_LOGGER.handlers:
                        handler.flush()
                    section = runner_module_ref.read_section_small(file_path, gguf_entry)
                    if section:
                        # SectionSmall object has entry.size_c (compressed) and entry.size_u (uncompressed)
                        size_c = section.entry.size_c if hasattr(section, 'entry') and hasattr(section.entry, 'size_c') else 0
                        size_u = section.entry.size_u if hasattr(section, 'entry') and hasattr(section.entry, 'size_u') else 0
                        EDITOR_LOGGER.info(f"[THREAD] GGUF section read successfully, compressed: {size_c} bytes, uncompressed: {size_u} bytes")
                        for handler in EDITOR_LOGGER.handlers:
                            handler.flush()
                        try:
                            # Decode and validate the section (passes SectionSmall object, returns bytes)
                            EDITOR_LOGGER.info("[THREAD] Decoding and validating GGUF section")
                            gguf_data = runner_module_ref.decode_and_validate(section)
                            EDITOR_LOGGER.info(f"[THREAD] GGUF section decoded successfully, size: {len(gguf_data)} bytes")
                            for handler in EDITOR_LOGGER.handlers:
                                handler.flush()
                            with open(temp_gguf_path, 'wb') as f:
                                f.write(gguf_data)
                        except Exception as e:
                            import traceback
                            error_msg = f"Failed to decode GGUF section: {e}"
                            error_code = "ERR-EDITOR-4DLLM-001"
                            traceback_str = traceback.format_exc()
                            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                            EDITOR_LOGGER.error(f"[THREAD] Failed to decode GGUF section: {e}", exc_info=True)
                            error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
                            return
                        
                        # Load metadata from 4DLLM (using cached module reference)
                        EDITOR_LOGGER.info("[THREAD] Loading metadata from 4DLLM")
                        for handler in EDITOR_LOGGER.handlers:
                            handler.flush()
                        metadata_entry = next((e for e in entries if e.section_type == runner_module_ref.SECTION_METADATA), None)
                        metadata = {}
                        if metadata_entry:
                            try:
                                meta_section = runner_module_ref.read_section_small(file_path, metadata_entry)
                                if meta_section:
                                    meta_data = runner_module_ref.decode_and_validate(meta_section)
                                    metadata = json.loads(meta_data.decode('utf-8'))
                                    EDITOR_LOGGER.info(f"[THREAD] 4DLLM metadata loaded successfully, keys: {list(metadata.keys())}")
                                    for handler in EDITOR_LOGGER.handlers:
                                        handler.flush()
                            except Exception as e:
                                EDITOR_LOGGER.warning(f"[THREAD] Failed to load 4DLLM metadata section: {e}, using empty metadata", exc_info=True)
                                for handler in EDITOR_LOGGER.handlers:
                                    handler.flush()
                                metadata = {}
                        
                        # Also load GGUF metadata (using cached module reference)
                        if editor_module_ref:
                            EDITOR_LOGGER.info("[THREAD] editor_module_ref is available, loading GGUF metadata")
                            for handler in EDITOR_LOGGER.handlers:
                                handler.flush()
                            try:
                                EDITOR_LOGGER.info(f"[THREAD] Creating GGUFEditor instance for: {temp_gguf_path}")
                                for handler in EDITOR_LOGGER.handlers:
                                    handler.flush()
                                
                                # CRITICAL: Suppress ALL logging before creating GGUFEditor
                                # The GGUFEditor constructor might log using handlers that access Qt
                                import logging
                                root_logger = logging.getLogger()
                                old_level = root_logger.level
                                
                                # Disable all logging temporarily to prevent Qt access from GGUFEditor's logging
                                root_logger.setLevel(logging.CRITICAL + 1)
                                
                                # Also disable handlers that might access Qt
                                original_handlers = []
                                for handler in list(root_logger.handlers):
                                    if hasattr(handler, 'gui_instance') or 'Qt' in str(type(handler)):
                                        original_handlers.append(handler)
                                        root_logger.removeHandler(handler)
                                
                                try:
                                    # Create editor instance - logging is suppressed
                                    editor = editor_module_ref.GGUFEditor(str(temp_gguf_path))
                                finally:
                                    # Restore logging and handlers
                                    root_logger.setLevel(old_level)
                                    for handler in original_handlers:
                                        root_logger.addHandler(handler)
                                
                                EDITOR_LOGGER.info("[THREAD] GGUFEditor instance created successfully")
                                for handler in EDITOR_LOGGER.handlers:
                                    handler.flush()
                                
                                # Load metadata - DO NOT use DEBUG_LOGGER from background thread (it might access Qt)
                                # Wrap in try-except to catch any Qt-related crashes from the editor's logging
                                try:
                                    EDITOR_LOGGER.info("[THREAD] Attempting to load metadata using load_metadata_only()")
                                    for handler in EDITOR_LOGGER.handlers:
                                        handler.flush()
                                    
                                    # Temporarily disable any Qt-related logging that might be triggered by GGUFEditor
                                    # The editor's logging might access Qt handlers
                                    import logging
                                    old_level = logging.getLogger().level
                                    logging.getLogger().setLevel(logging.CRITICAL + 1)  # Disable all logging temporarily
                                    
                                    try:
                                        gguf_metadata = editor.load_metadata_only() if hasattr(editor, 'load_metadata_only') else editor.load()
                                    finally:
                                        # Restore logging
                                        logging.getLogger().setLevel(old_level)
                                    
                                    EDITOR_LOGGER.info(f"[THREAD] Metadata loaded successfully, type: {type(gguf_metadata)}")
                                    for handler in EDITOR_LOGGER.handlers:
                                        handler.flush()
                                except Exception as e:
                                    # Use EDITOR_LOGGER instead of DEBUG_LOGGER to avoid Qt access
                                    EDITOR_LOGGER.warning(f"[THREAD] Failed to load metadata-only: {e}, trying full load")
                                    for handler in EDITOR_LOGGER.handlers:
                                        handler.flush()
                                    # Disable logging temporarily for editor.load() too
                                    import logging
                                    old_level = logging.getLogger().level
                                    logging.getLogger().setLevel(logging.CRITICAL + 1)
                                    
                                    try:
                                        gguf_metadata = editor.load()
                                        logging.getLogger().setLevel(old_level)  # Restore logging
                                        EDITOR_LOGGER.info("[THREAD] Metadata loaded via full load()")
                                        for handler in EDITOR_LOGGER.handlers:
                                            handler.flush()
                                    except Exception:
                                        logging.getLogger().setLevel(old_level)  # Restore logging even on error
                                        raise
                                    except Exception as e2:
                                        EDITOR_LOGGER.error(f"[THREAD] Failed to load GGUF metadata: {e2}", exc_info=True)
                                        for handler in EDITOR_LOGGER.handlers:
                                            handler.flush()
                                        gguf_metadata = {}
                                
                                # Ensure metadata is a dict
                                if not isinstance(gguf_metadata, dict):
                                    EDITOR_LOGGER.warning(f"[THREAD] GGUF metadata is not a dict: {type(gguf_metadata)}, using empty dict")
                                    for handler in EDITOR_LOGGER.handlers:
                                        handler.flush()
                                    gguf_metadata = {}
                                
                                # Merge metadata (4DLLM metadata takes precedence)
                                if not isinstance(metadata, dict):
                                    metadata = {}
                                metadata.update(gguf_metadata)
                                
                                # Store editor instance and file paths on main thread using signal (using cached signal reference)
                                EDITOR_LOGGER.info("[THREAD] About to emit storeEditorInstanceRequested signal (thread-safe, using cached reference)")
                                store_editor_signal.emit(editor, str(file_path), str(temp_gguf_path), metadata, "4dllm")
                                EDITOR_LOGGER.info("[THREAD] storeEditorInstanceRequested signal emitted successfully")
                                
                                # Update UI on main thread using signals
                                EDITOR_LOGGER.info("[THREAD] About to emit displayEditorMetadataRequested signal (thread-safe)")
                                metadata_signal.emit(metadata, str(file_path))
                                EDITOR_LOGGER.info("[THREAD] displayEditorMetadataRequested signal emitted successfully")
                                
                                file_size_mb = file_path.stat().st_size / (1024*1024)
                                EDITOR_LOGGER.info("[THREAD] About to call _execute_js_safe (thread-safe)")
                                execute_js_method(f"document.getElementById('editor-file-info').textContent = 'File: {file_path.name} ({file_size_mb:.2f} MB)';")
                                EDITOR_LOGGER.info("[THREAD] _execute_js_safe completed successfully")
                                EDITOR_LOGGER.info("[THREAD] _load_4dllm_for_editing() completed successfully")
                            except Exception as e:
                                import traceback
                                error_msg = f"Failed to create editor instance: {e}"
                                error_code = "ERR-EDITOR-4DLLM-002"
                                traceback_str = traceback.format_exc()
                                # Use EDITOR_LOGGER instead of DEBUG_LOGGER to avoid Qt access from background thread
                                EDITOR_LOGGER.error(f"[THREAD] Failed to create editor instance: {e}", exc_info=True)
                                for handler in EDITOR_LOGGER.handlers:
                                    handler.flush()
                                error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
                        else:
                            error_msg = "Editor module not available"
                            EDITOR_LOGGER.warning("[THREAD] Editor module not available")
                            error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-4DLLM-003")
                    else:
                        error_msg = "Failed to read GGUF section"
                        EDITOR_LOGGER.warning("[THREAD] Failed to read GGUF section")
                        error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-4DLLM-004")
                else:
                    error_msg = "No GGUF section found in 4DLLM file"
                    EDITOR_LOGGER.warning("[THREAD] No GGUF section found in 4DLLM file")
                    error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-4DLLM-005")
        except Exception as e:
            import traceback
            EDITOR_LOGGER.error(f"[THREAD] UNHANDLED EXCEPTION in _load_4dllm_for_editing: {e}", exc_info=True)
            error_msg = f"Unexpected error loading 4DLLM file: {e}"
            error_code = "ERR-EDITOR-4DLLM-999"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
        finally:
            EDITOR_LOGGER.info(f"[THREAD] _load_4dllm_for_editing() exiting (finally block)")
            EDITOR_LOGGER.info("=" * 80)
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()
    
    def _load_gguf_for_editing(self, file_path: Path, terminal_signal=None, error_signal=None, metadata_signal=None, store_editor_signal=None, execute_js_method=None, editor_module_ref=None, runner_module_ref=None):
        """Load GGUF file for editing - runs in background thread"""
        import threading
        import traceback
        
        # Use provided signal references to avoid accessing 'self' from background thread
        if terminal_signal is None:
            terminal_signal = self.terminalAppendRequested
        if error_signal is None:
            error_signal = self.showErrorDialogRequested
        if metadata_signal is None:
            metadata_signal = self.displayEditorMetadataRequested
        if store_editor_signal is None:
            store_editor_signal = self.storeEditorInstanceRequested
        if execute_js_method is None:
            execute_js_method = self._execute_js_safe
        # Use provided module reference instead of global variable
        if editor_module_ref is None:
            editor_module_ref = editor_module
        if runner_module_ref is None:
            runner_module_ref = runner_module
        
        EDITOR_LOGGER.info("=" * 80)
        EDITOR_LOGGER.info(f"[THREAD] _load_gguf_for_editing() called - Thread: {threading.current_thread().ident}, Name: {threading.current_thread().name}")
        EDITOR_LOGGER.info(f"[THREAD] File path: {file_path}")
        EDITOR_LOGGER.info(f"[THREAD] editor_module_ref is None: {editor_module_ref is None}")
        for handler in EDITOR_LOGGER.handlers:
            handler.flush()
        
        if not editor_module_ref:
            error_msg = "Editor module not available!"
            EDITOR_LOGGER.error(f"[THREAD] {error_msg}")
            error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-001")
            execute_js_method("document.getElementById('editor-file-info').textContent = 'Error: Editor module not available';")
            return
        
        try:
            # Safety: detect 4DLLM magic even if extension is .gguf
            try:
                with open(file_path, "rb") as f:
                    magic = f.read(4)
                four_magic = runner_module_ref.FOURDLLM_MAGIC if runner_module_ref and hasattr(runner_module_ref, "FOURDLLM_MAGIC") else b"4DLL"
                if magic == four_magic:
                    EDITOR_LOGGER.warning("[THREAD] GGUF loader detected 4DLLM magic; routing to 4DLLM loader")
                    self._load_4dllm_for_editing(file_path, terminal_signal, error_signal, metadata_signal, store_editor_signal, execute_js_method, runner_module_ref, editor_module_ref)
                    return
            except Exception as magic_err:
                EDITOR_LOGGER.warning(f"[THREAD] Failed to inspect file magic for GGUF: {magic_err}")

            terminal_signal.emit(f"Loading GGUF file: {file_path.name}\n", "info")
            EDITOR_LOGGER.info("[THREAD] Terminal signal emitted")
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()

            # Load metadata in a subprocess to prevent native GGUF crashes from killing the UI.
            try:
                metadata = self._load_gguf_metadata_subprocess(file_path)
                if not isinstance(metadata, dict):
                    metadata = {}
                # Store editor instance as None (no in-process editor), but still allow metadata edit/save.
                EDITOR_LOGGER.info("[THREAD] GGUF metadata loaded via subprocess")
                store_editor_signal.emit(None, str(file_path), None, metadata, "gguf")
                metadata_signal.emit(metadata, str(file_path))
                try:
                    file_size_mb = file_path.stat().st_size / (1024 * 1024)
                    file_size_str = f"{file_size_mb:.2f} MB"
                except Exception as size_err:
                    EDITOR_LOGGER.warning(f"[THREAD] Failed to get file size: {size_err}")
                    file_size_str = "Unknown size"
                execute_js_method(f"document.getElementById('editor-file-info').textContent = 'File: {file_path.name} ({file_size_str})';")
                EDITOR_LOGGER.info("[THREAD] _load_gguf_for_editing() completed successfully (subprocess)")
                return
            except Exception as sub_err:
                EDITOR_LOGGER.error(f"[THREAD] Subprocess GGUF load failed: {sub_err}", exc_info=True)
                # Fall back to in-process GGUFEditor only if subprocess failed.
            
            # Create editor instance (using cached module reference)
            try:
                EDITOR_LOGGER.info(f"[THREAD] Creating GGUFEditor instance")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
                
                # CRITICAL: Suppress ALL logging before creating GGUFEditor
                # The GGUFEditor constructor might log using handlers that access Qt
                import logging
                root_logger = logging.getLogger()
                old_level = root_logger.level
                
                # Disable all logging temporarily to prevent Qt access from GGUFEditor's logging
                root_logger.setLevel(logging.CRITICAL + 1)
                
                # Also disable handlers that might access Qt
                original_handlers = []
                for handler in list(root_logger.handlers):
                    if hasattr(handler, 'gui_instance') or 'Qt' in str(type(handler)):
                        original_handlers.append(handler)
                        root_logger.removeHandler(handler)
                
                try:
                    # Create editor instance - logging is suppressed
                    editor = editor_module_ref.GGUFEditor(str(file_path))
                finally:
                    # Restore logging and handlers
                    root_logger.setLevel(old_level)
                    for handler in original_handlers:
                        root_logger.addHandler(handler)
                
                EDITOR_LOGGER.info("[THREAD] GGUFEditor instance created successfully")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
            except Exception as e:
                error_msg = f"Failed to create GGUF editor instance: {e}"
                error_code = "ERR-EDITOR-002"
                traceback_str = traceback.format_exc()
                EDITOR_LOGGER.error(f"[THREAD] {error_msg}", exc_info=True)
                error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
                execute_js_method("document.getElementById('editor-file-info').textContent = 'Error: Failed to create editor';")
                return
            
            # Load metadata
            try:
                EDITOR_LOGGER.info("[THREAD] Loading metadata from GGUF file")
                metadata = editor.load_metadata_only() if hasattr(editor, 'load_metadata_only') else editor.load()
                EDITOR_LOGGER.info(f"[THREAD] Metadata loaded successfully, type: {type(metadata)}")
                for handler in EDITOR_LOGGER.handlers:
                    handler.flush()
            except Exception as e:
                EDITOR_LOGGER.warning(f"[THREAD] Failed to load metadata-only, trying full load: {e}", exc_info=True)
                try:
                    metadata = editor.load()
                    EDITOR_LOGGER.info("[THREAD] Metadata loaded via full load")
                except Exception as e2:
                    error_msg = f"Failed to load GGUF metadata: {e2}"
                    error_code = "ERR-EDITOR-003"
                    traceback_str = traceback.format_exc()
                    EDITOR_LOGGER.error(f"[THREAD] {error_msg}", exc_info=True)
                    error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
                    execute_js_method("document.getElementById('editor-file-info').textContent = 'Error: Failed to load metadata';")
                    return
            
            # Ensure metadata is a dict
            if not isinstance(metadata, dict):
                EDITOR_LOGGER.warning(f"[THREAD] Metadata is not a dict: {type(metadata)}, converting to empty dict")
                metadata = {}
            
            # Store editor instance and file path on main thread using signal (using cached signal reference)
            EDITOR_LOGGER.info("[THREAD] About to emit storeEditorInstanceRequested signal (thread-safe, using cached reference)")
            store_editor_signal.emit(editor, str(file_path), None, metadata, "gguf")
            EDITOR_LOGGER.info("[THREAD] storeEditorInstanceRequested signal emitted successfully")
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()
            
            # Update UI on main thread using signals
            EDITOR_LOGGER.info("[THREAD] About to emit displayEditorMetadataRequested signal (thread-safe)")
            metadata_signal.emit(metadata, str(file_path))
            EDITOR_LOGGER.info("[THREAD] displayEditorMetadataRequested signal emitted successfully")
            
            # Update file info
            try:
                file_size_mb = file_path.stat().st_size / (1024*1024)
                file_size_str = f"{file_size_mb:.2f} MB"
            except Exception as size_err:
                EDITOR_LOGGER.warning(f"[THREAD] Failed to get file size: {size_err}")
                file_size_str = "Unknown size"
            
            EDITOR_LOGGER.info("[THREAD] About to call _execute_js_safe for file info (thread-safe)")
            execute_js_method(f"document.getElementById('editor-file-info').textContent = 'File: {file_path.name} ({file_size_str})';")
            EDITOR_LOGGER.info("[THREAD] _execute_js_safe completed successfully")
            EDITOR_LOGGER.info("[THREAD] _load_gguf_for_editing() completed successfully")
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()
        except Exception as e:
            import traceback
            EDITOR_LOGGER.error(f"[THREAD] UNHANDLED EXCEPTION in _load_gguf_for_editing: {e}", exc_info=True)
            error_msg = f"Unexpected error loading GGUF for editing: {e}"
            error_code = "ERR-EDITOR-004"
            traceback_str = traceback.format_exc()
            error_signal.emit("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
            execute_js_method("document.getElementById('editor-file-info').textContent = 'Error loading file';")
            execute_js_method("document.getElementById('editor-content').innerHTML = '<div style=\"padding: 40px; text-align: center; color: #f87171;\"><div style=\"font-size: 18px; margin-bottom: 12px;\">❌ Error Loading File</div><div style=\"font-size: 13px;\">Please check the logs for details</div></div>';")
        except BaseException as e:
            # Catch fatal exceptions (e.g., SystemExit) to prevent full app crash
            try:
                EDITOR_LOGGER.error(f"[THREAD] FATAL EXCEPTION in _load_gguf_for_editing: {e}", exc_info=True)
                error_msg = f"Fatal error loading GGUF for editing: {e}"
                error_signal.emit("Editor Error", error_msg, "ERR-EDITOR-004-FATAL")
                execute_js_method("document.getElementById('editor-file-info').textContent = 'Error loading file';")
                execute_js_method("document.getElementById('editor-content').innerHTML = '<div style=\"padding: 40px; text-align: center; color: #f87171;\"><div style=\"font-size: 18px; margin-bottom: 12px;\">❌ Error Loading File</div><div style=\"font-size: 13px;\">Please check the logs for details</div></div>';")
            except Exception:
                pass
        finally:
            EDITOR_LOGGER.info(f"[THREAD] _load_gguf_for_editing() exiting (finally block)")
            EDITOR_LOGGER.info("=" * 80)
            for handler in EDITOR_LOGGER.handlers:
                handler.flush()

    def _load_gguf_metadata_subprocess(self, file_path: Path) -> Dict[str, Any]:
        """Load GGUF metadata in a separate process to avoid native crashes."""
        import subprocess
        import sys
        import textwrap

        code = textwrap.dedent(
            """
            import json
            import sys
            from pathlib import Path

            def normalize(value):
                if isinstance(value, bytes):
                    return value.decode("utf-8", errors="replace")
                if isinstance(value, (int, float, bool, str)) or value is None:
                    return value
                if isinstance(value, (list, tuple)):
                    return [normalize(v) for v in value]
                if isinstance(value, dict):
                    return {str(k): normalize(v) for k, v in value.items()}
                return str(value)

            path = Path(sys.argv[1])
            import gguf
            reader = gguf.GGUFReader(str(path))
            keys = []
            if hasattr(reader, "fields") and hasattr(reader.fields, "keys"):
                keys = list(reader.fields.keys())
            metadata = {}
            for key in keys:
                value = None
                if hasattr(reader, "get_field"):
                    field = reader.get_field(key)
                    if field is not None:
                        try:
                            value = field.contents()
                        except Exception:
                            value = None
                if value is None and hasattr(reader, "fields"):
                    try:
                        field = reader.fields.get(key)
                        if field is not None:
                            if hasattr(field, "contents"):
                                value = field.contents()
                            elif hasattr(field, "parts") and field.parts:
                                value = field.parts[0]
                    except Exception:
                        value = None
                if value is None and hasattr(reader, "get_value"):
                    try:
                        value = reader.get_value(key)
                    except Exception:
                        value = None
                metadata[str(key)] = normalize(value)
            print(json.dumps(metadata, ensure_ascii=False))
            """
        ).strip()

        proc = subprocess.run(
            [sys.executable, "-c", code, str(file_path)],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            timeout=120,
        )
        if proc.returncode != 0:
            raise RuntimeError(proc.stderr.strip() or "GGUF subprocess failed")
        try:
            return json.loads(proc.stdout or "{}")
        except Exception as e:
            raise RuntimeError(f"Failed to parse GGUF subprocess output: {e}")
    
    def _display_editor_metadata_safe(self, metadata_obj: object, file_path_str: str):
        """Safe wrapper to display editor metadata from any thread."""
        try:
            fp = Path(file_path_str) if file_path_str else None
            if fp is None:
                return
            # Ensure metadata is a dict
            if isinstance(metadata_obj, dict):
                metadata = metadata_obj
            else:
                DEBUG_LOGGER.warning(f"Metadata is not a dict in _display_editor_metadata_safe: {type(metadata_obj)}, using empty dict")
                metadata = {}
            self._display_editor_metadata(metadata, fp)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error in _display_editor_metadata_safe: {e}", exc_info=True)
    
    def _store_editor_instance_safe(self, editor_obj, file_path_obj, temp_gguf_obj, metadata_obj, file_type_str):
        """Safe wrapper to store editor instance from any thread."""
        try:
            # Convert string paths to Path objects if needed
            if isinstance(file_path_obj, str):
                file_path_obj = Path(file_path_obj)
            if isinstance(temp_gguf_obj, str):
                temp_gguf_obj = Path(temp_gguf_obj)
            
            # Store on main thread (this is safe because signal is connected to main thread)
            self._editor_instance = editor_obj
            self._editor_file_path = file_path_obj
            self._editor_temp_gguf = temp_gguf_obj
            self._editor_metadata = metadata_obj if isinstance(metadata_obj, dict) else {}
            self._editor_file_type = file_type_str
            EDITOR_LOGGER.info(f"[MAIN] Editor instance stored safely: {self._editor_instance is not None}")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error in _store_editor_instance_safe: {e}", exc_info=True)
            EDITOR_LOGGER.error(f"[MAIN] Error storing editor instance: {e}", exc_info=True)
    
    def _display_editor_metadata(self, metadata: Dict[str, Any], file_path: Path):
        """Display metadata in editor UI - supports easy and advanced modes"""
        # Ensure metadata is a dict
        if not isinstance(metadata, dict):
            DEBUG_LOGGER.warning(f"Metadata is not a dict in _display_editor_metadata: {type(metadata)}, using empty dict")
            metadata = {}
        
        # Default to easy mode - mode switching is handled via refreshEditorDisplay
        # which is called when the user clicks the mode buttons
        mode = getattr(self, '_editor_mode', 'easy')
        
        try:
            if mode == 'advanced':
                self._display_advanced_mode(metadata, file_path)
            else:
                self._display_easy_mode(metadata, file_path)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error in _display_editor_metadata: {e}", exc_info=True)
            error_html = f"<div style='padding: 40px; text-align: center; color: #f87171;'><div style='font-size: 18px; margin-bottom: 12px;'>❌ Error Displaying Editor</div><div style='font-size: 13px;'>{str(e)}</div></div>"
            self._execute_js(f"document.getElementById('editor-content').innerHTML = {json.dumps(error_html)};")
    
    def _display_easy_mode(self, metadata: Dict[str, Any], file_path: Path):
        """Display easy mode editor with system prompt and persona prompt"""
        try:
            # Get file name safely
            file_name = file_path.name if file_path and hasattr(file_path, 'name') else str(file_path) if file_path else "Unknown"
            
            # Extract system and persona prompts
            system_prompt = metadata.get('llama.system_prompt') or metadata.get('chat_template.system_prompt') or ''
            persona_prompt = metadata.get('llama.persona_prompt') or metadata.get('chat_template.persona_prompt') or ''
            
            # Convert to strings if needed
            if not isinstance(system_prompt, str):
                system_prompt = str(system_prompt) if system_prompt else ''
            if not isinstance(persona_prompt, str):
                persona_prompt = str(persona_prompt) if persona_prompt else ''
            
            # Escape HTML in prompts for safe display
            def escape_html(text):
                return str(text).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#x27;')
            
            system_prompt_escaped = escape_html(system_prompt)
            persona_prompt_escaped = escape_html(persona_prompt)
            
            file_key_json = json.dumps(file_name)
            html = f"""
        <div style='padding: 20px; color: #cbd5e1;'>
            <h3 style='color: #06b6d4; margin-bottom: 20px;'>✨ Easy Editor Mode - {escape_html(file_name)}</h3>
            
            <div style='margin-bottom: 24px; padding: 16px; background: rgba(6, 182, 212, 0.1); border: 1px solid rgba(6, 182, 212, 0.3); border-radius: 10px;'>
                <div style='font-size: 13px; color: #06b6d4; margin-bottom: 8px; font-weight: 600;'>⚙️ System Prompt</div>
                <div style='font-size: 11px; color: #94a3b8; margin-bottom: 12px;'>Define the system behavior and instructions</div>
                <textarea id='editor-system-prompt' style='width: 100%; min-height: 120px; padding: 12px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; font-family: monospace; font-size: 13px; resize: vertical;'>{system_prompt_escaped}</textarea>
                <div style='display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap;'>
                    <button class='btn-secondary' onclick="saveEditorPrompt('system')" style='padding: 6px 10px; font-size: 11px; font-weight: 700;'>💾 Save Prompt</button>
                    <button class='btn-secondary' onclick="loadEditorPrompt('system')" style='padding: 6px 10px; font-size: 11px; font-weight: 700;'>🔄 Load Saved</button>
                </div>
                <div id='system-prompt-count' style='font-size: 11px; color: #64748b; margin-top: 4px; text-align: right;'>{len(system_prompt)} characters</div>
            </div>
            
            <div style='margin-bottom: 24px; padding: 16px; background: rgba(6, 182, 212, 0.1); border: 1px solid rgba(6, 182, 212, 0.3); border-radius: 10px;'>
                <div style='font-size: 13px; color: #06b6d4; margin-bottom: 8px; font-weight: 600;'>👤 Persona Prompt</div>
                <div style='font-size: 11px; color: #94a3b8; margin-bottom: 12px;'>Define the character persona and personality</div>
                <textarea id='editor-persona-prompt' style='width: 100%; min-height: 120px; padding: 12px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; font-family: monospace; font-size: 13px; resize: vertical;'>{persona_prompt_escaped}</textarea>
                <div style='display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap;'>
                    <button class='btn-secondary' onclick="saveEditorPrompt('persona')" style='padding: 6px 10px; font-size: 11px; font-weight: 700;'>💾 Save Prompt</button>
                    <button class='btn-secondary' onclick="loadEditorPrompt('persona')" style='padding: 6px 10px; font-size: 11px; font-weight: 700;'>🔄 Load Saved</button>
                </div>
                <div id='persona-prompt-count' style='font-size: 11px; color: #64748b; margin-top: 4px; text-align: right;'>{len(persona_prompt)} characters</div>
            </div>
            
            <div style='padding: 12px; background: rgba(15, 23, 42, 0.4); border-radius: 8px; border: 1px solid rgba(71, 85, 105, 0.3);'>
                <div style='font-size: 12px; color: #94a3b8; line-height: 1.6;'>
                    💡 <strong>Tip:</strong> Changes will be saved to both <code>llama.system_prompt</code> and <code>chat_template.system_prompt</code> (and similarly for persona prompts) for maximum compatibility.
                </div>
            </div>
        </div>
        
        <script>
            const editorFileKey = {file_key_json};
            function editorPromptKey(kind) {{
                return `editor_prompt_${{kind}}::${{editorFileKey || 'default'}}`;
            }}
            function saveEditorPrompt(kind) {{
                try {{
                    const el = document.getElementById(kind === 'system' ? 'editor-system-prompt' : 'editor-persona-prompt');
                    if (!el) return;
                    localStorage.setItem(editorPromptKey(kind), el.value || '');
                }} catch (e) {{
                    console.warn('Failed to save editor prompt', e);
                }}
            }}
            function loadEditorPrompt(kind) {{
                try {{
                    const el = document.getElementById(kind === 'system' ? 'editor-system-prompt' : 'editor-persona-prompt');
                    if (!el) return;
                    const saved = localStorage.getItem(editorPromptKey(kind)) || '';
                    el.value = saved;
                    updateCounts();
                }} catch (e) {{
                    console.warn('Failed to load editor prompt', e);
                }}
            }}
            // Update character counts
            function updateCounts() {{
                const systemEl = document.getElementById('editor-system-prompt');
                const personaEl = document.getElementById('editor-persona-prompt');
                if (systemEl) {{
                    document.getElementById('system-prompt-count').textContent = systemEl.value.length + ' characters';
                }}
                if (personaEl) {{
                    document.getElementById('persona-prompt-count').textContent = personaEl.value.length + ' characters';
                }}
            }}
            
            const systemEl = document.getElementById('editor-system-prompt');
            const personaEl = document.getElementById('editor-persona-prompt');
            if (systemEl) {{
                systemEl.addEventListener('input', updateCounts);
            }}
            if (personaEl) {{
                personaEl.addEventListener('input', updateCounts);
            }}
        </script>
        """
            
            self._execute_js(f"document.getElementById('editor-content').innerHTML = {json.dumps(html)};")
        except Exception as e:
            import traceback
            error_msg = f"Error displaying easy mode: {e}"
            error_code = "ERR-EDITOR-EASY-001"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            error_html = f"<div style='padding: 40px; text-align: center; color: #f87171;'><div style='font-size: 18px; margin-bottom: 12px;'>❌ Error Displaying Easy Mode</div><div style='font-size: 13px;'>{str(e)}</div></div>"
            try:
                self._execute_js(f"document.getElementById('editor-content').innerHTML = {json.dumps(error_html)};")
            except:
                pass
    
    def _display_advanced_mode(self, metadata: Dict[str, Any], file_path: Path):
        """Display advanced mode editor with full metadata tree and hack features"""
        try:
            import html as html_lib
            # Get file name safely
            file_name = file_path.name if file_path and hasattr(file_path, 'name') else str(file_path) if file_path else "Unknown"
            
            # Escape HTML in values
            def escape_html(text):
                return str(text).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#x27;')
            
            def format_value(v):
                if isinstance(v, (dict, list)):
                    return json.dumps(v, indent=2)
                return str(v)
            
            # Create comprehensive advanced mode HTML with hack features
            html = f"""
        <div style='padding: 20px; color: #cbd5e1;'>
            <div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;'>
                <h3 style='color: #06b6d4; margin: 0;'>🔧 Advanced Mode - Metadata Editor (Hack Mode)</h3>
                <div style='padding: 6px 12px; background: rgba(251, 191, 36, 0.2); border: 1px solid rgba(251, 191, 36, 0.4); border-radius: 6px; font-size: 11px; color: #fbbf24; font-weight: 600;'>⚠️ DIRECT METADATA EDITING</div>
            </div>
            
            <!-- Toolbar -->
            <div style='display: flex; gap: 8px; margin-bottom: 16px; padding: 12px; background: rgba(15, 23, 42, 0.4); border-radius: 8px; border: 1px solid rgba(71, 85, 105, 0.3);'>
                <button onclick="window.bridge.advancedRefreshMetadata()" style='padding: 8px 16px; background: linear-gradient(135deg, rgba(51, 65, 85, 0.8), rgba(71, 85, 105, 0.8)); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0; font-weight: 600; cursor: pointer;'>🔄 Refresh Metadata</button>
                <button onclick="window.bridge.advancedAddKey()" style='padding: 8px 16px; background: linear-gradient(135deg, rgba(34, 211, 57, 0.2), rgba(34, 211, 57, 0.3)); border: 1px solid rgba(34, 211, 57, 0.5); border-radius: 6px; color: #34d399; font-weight: 600; cursor: pointer;'>➕ Add Key</button>
                <button onclick="window.bridge.advancedDeleteKey()" style='padding: 8px 16px; background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(239, 68, 68, 0.3)); border: 1px solid rgba(239, 68, 68, 0.5); border-radius: 6px; color: #f87171; font-weight: 600; cursor: pointer;'>➖ Delete Selected</button>
            </div>
            
            <!-- Search Bar -->
            <div style='margin-bottom: 16px;'>
                <input type='text' id='advanced-search' placeholder='🔍 Search metadata keys or values...' oninput='advancedFilterMetadata()' style='width: 100%; padding: 10px 16px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 8px; color: #e2e8f0; font-size: 13px;'>
            </div>
            
            <!-- Metadata Tree View -->
            <div style='background: rgba(15, 23, 42, 0.4); border-radius: 8px; border: 1px solid rgba(71, 85, 105, 0.3); overflow: hidden; margin-bottom: 16px;'>
                <div style='display: grid; grid-template-columns: 2fr 3fr 1fr; padding: 12px; background: rgba(6, 182, 212, 0.1); border-bottom: 1px solid rgba(71, 85, 105, 0.3); font-weight: 600; color: #06b6d4; font-size: 12px;'>
                    <div>Key</div>
                    <div>Value</div>
                    <div>Type</div>
                </div>
                <div id='advanced-metadata-tree' style='max-height: 400px; overflow-y: auto;'>
        """
            
            # Add metadata items
            for key, value in sorted(metadata.items()):
                value_str = format_value(value)
                if len(value_str) > 150:
                    value_str = value_str[:147] + "..."
                
                value_type = type(value).__name__
                if isinstance(value, list):
                    value_type = f"array[{len(value)}]"
                elif isinstance(value, dict):
                    value_type = f"dict[{len(value)}]"
                
                value_display = escape_html(value_str)
                key_display = escape_html(key)
                
                key_json = html_lib.escape(json.dumps(key))
                value_json = html_lib.escape(json.dumps(value))
                html += f"""
                    <div class='metadata-row' data-key-json='{key_json}' data-value-json='{value_json}' style='display: grid; grid-template-columns: 2fr 3fr 1fr; padding: 10px 12px; border-bottom: 1px solid rgba(71, 85, 105, 0.2); cursor: pointer; transition: background 0.2s;'>
                        <div style='font-weight: 600; color: #22d3ee; font-family: monospace; font-size: 12px;'>{key_display}</div>
                        <div style='color: #94a3b8; font-family: monospace; font-size: 11px; white-space: pre-wrap; word-break: break-word;'>{value_display}</div>
                        <div style='color: #64748b; font-size: 11px; text-align: right;'>{value_type}</div>
                    </div>
                """
            
            html += """
                </div>
            </div>
            
            <!-- Edit Panel -->
            <div style='background: rgba(15, 23, 42, 0.4); border-radius: 8px; border: 1px solid rgba(71, 85, 105, 0.3); padding: 16px;'>
                <div style='font-weight: 600; color: #06b6d4; margin-bottom: 12px; font-size: 14px;'>✏️ Edit Selected Item</div>
                
                <div style='margin-bottom: 12px;'>
                    <label style='display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;'>Key:</label>
                    <input type='text' id='advanced-edit-key' placeholder='Metadata key name' style='width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0; font-family: monospace; font-size: 13px;'>
                </div>
                
                <div style='margin-bottom: 12px;'>
                    <label style='display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;'>Value:</label>
                    <textarea id='advanced-edit-value' placeholder='Metadata value (JSON for arrays/objects)' style='width: 100%; min-height: 120px; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0; font-family: monospace; font-size: 12px; resize: vertical;'></textarea>
                </div>
                
                <div style='margin-bottom: 16px;'>
                    <label style='display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px;'>Type:</label>
                    <select id='advanced-edit-type' style='width: 100%; padding: 10px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(71, 85, 105, 0.5); border-radius: 6px; color: #e2e8f0; font-size: 13px;'>
                        <option value='string'>String</option>
                        <option value='int'>Integer</option>
                        <option value='float'>Float</option>
                        <option value='bool'>Boolean</option>
                        <option value='array'>Array (JSON)</option>
                    </select>
                </div>
                
                <button onclick='advancedSaveItemJS()' style='width: 100%; padding: 12px; background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #06b6d4 100%); border: none; border-radius: 8px; color: #ffffff; font-weight: 600; cursor: pointer; font-size: 13px;'>💾 Save Item</button>
            </div>
            
            <!-- Info Panel -->
            <div style='margin-top: 16px; padding: 12px; background: rgba(6, 182, 212, 0.1); border: 1px solid rgba(6, 182, 212, 0.3); border-radius: 8px;'>
                <div style='font-size: 12px; color: #94a3b8; line-height: 1.6;'>
                    <strong style='color: #06b6d4;'>💡 Advanced Mode Features:</strong><br>
                    • <strong>Double-click</strong> any row to edit it<br>
                    • <strong>Add Key:</strong> Create new metadata keys (hack: add custom keys!)<br>
                    • <strong>Delete Key:</strong> Remove unwanted metadata (hack: clean up GGUF!)<br>
                    • <strong>Edit Values:</strong> Modify any metadata value with type conversion<br>
                    • <strong>Search:</strong> Quickly find keys or values in large metadata<br>
                    • <strong>Type Support:</strong> String, Int, Float, Bool, and JSON Arrays<br>
                    <span style='color: #fbbf24; font-weight: 600;'>⚠️ Warning: Direct metadata editing can break compatibility if misused!</span>
                </div>
            </div>
        </div>
        
        """
        
            self._execute_js(f"document.getElementById('editor-content').innerHTML = {json.dumps(html)};")
            self._execute_js(
                f"""
                (function() {{
                    window.advancedMetadataData = {json.dumps(metadata)};
                    window.advancedSelectRow = function(element, key, value) {{
                        document.querySelectorAll('.metadata-row').forEach(row => {{
                            row.classList.remove('selected');
                            row.style.background = 'transparent';
                        }});
                        element.classList.add('selected');
                        element.style.background = 'rgba(6, 182, 212, 0.2)';
                        const keyEl = document.getElementById('advanced-edit-key');
                        const valueEl = document.getElementById('advanced-edit-value');
                        const typeEl = document.getElementById('advanced-edit-type');
                        if (!keyEl || !valueEl || !typeEl) return;
                        keyEl.value = key;
                        let valueStr = '';
                        let valueType = 'string';
                        if (Array.isArray(value)) {{
                            valueStr = JSON.stringify(value, null, 2);
                            valueType = 'array';
                        }} else if (typeof value === 'object' && value !== null) {{
                            valueStr = JSON.stringify(value, null, 2);
                            valueType = 'array';
                        }} else if (typeof value === 'number') {{
                            valueStr = String(value);
                            valueType = Number.isInteger(value) ? 'int' : 'float';
                        }} else if (typeof value === 'boolean') {{
                            valueStr = String(value);
                            valueType = 'bool';
                        }} else {{
                            valueStr = String(value);
                            valueType = 'string';
                        }}
                        valueEl.value = valueStr;
                        typeEl.value = valueType;
                    }};
                    window.advancedFilterMetadata = function() {{
                        const searchEl = document.getElementById('advanced-search');
                        const searchText = (searchEl ? searchEl.value : '').toLowerCase();
                        document.querySelectorAll('.metadata-row').forEach(row => {{
                            const keyJson = row.getAttribute('data-key-json') || '\"\"';
                            const valueText = row.textContent.toLowerCase();
                            let key = '';
                            try {{
                                key = JSON.parse(keyJson);
                            }} catch (e) {{
                                key = '';
                            }}
                            const keyText = String(key).toLowerCase();
                            if (searchText === '' || keyText.includes(searchText) || valueText.includes(searchText)) {{
                                row.style.display = 'grid';
                            }} else {{
                                row.style.display = 'none';
                            }}
                        }});
                    }};
                    window.advancedSaveItemJS = function() {{
                        const keyEl = document.getElementById('advanced-edit-key');
                        const valueEl = document.getElementById('advanced-edit-value');
                        const typeEl = document.getElementById('advanced-edit-type');
                        if (!keyEl || !valueEl || !typeEl) return;
                        const key = keyEl.value.trim();
                        if (!key) {{
                            alert('⚠️ Key cannot be empty!');
                            return;
                        }}
                        const valueText = valueEl.value.trim();
                        const valueType = typeEl.value;
                        if (window.bridge && window.bridge.advancedSaveItemWithData) {{
                            window.bridge.advancedSaveItemWithData(key, valueText, valueType);
                        }} else {{
                            alert('⚠️ Bridge not connected. Please refresh the page.');
                        }}
                    }};
                    document.querySelectorAll('.metadata-row').forEach(row => {{
                        row.addEventListener('click', function() {{
                            const keyJson = this.getAttribute('data-key-json') || '\"\"';
                            const valueJson = this.getAttribute('data-value-json') || 'null';
                            let key = '';
                            let value = null;
                            try {{ key = JSON.parse(keyJson); }} catch (e) {{ key = ''; }}
                            try {{ value = JSON.parse(valueJson); }} catch (e) {{ value = null; }}
                            window.advancedSelectRow(this, key, value);
                        }});
                        row.addEventListener('mouseover', function() {{
                            if (!this.classList.contains('selected')) {{
                                this.style.background = 'rgba(6, 182, 212, 0.1)';
                            }}
                        }});
                        row.addEventListener('mouseout', function() {{
                            if (!this.classList.contains('selected')) {{
                                this.style.background = 'transparent';
                            }}
                        }});
                        row.addEventListener('dblclick', function() {{
                            const keyJson = this.getAttribute('data-key-json') || '\"\"';
                            const valueJson = this.getAttribute('data-value-json') || 'null';
                            let key = '';
                            let value = null;
                            try {{ key = JSON.parse(keyJson); }} catch (e) {{ key = ''; }}
                            try {{ value = JSON.parse(valueJson); }} catch (e) {{ value = null; }}
                            window.advancedSelectRow(this, key, value);
                        }});
                    }});
                }})();
                """
            )
        except Exception as e:
            import traceback
            error_msg = f"Error displaying advanced mode: {e}"
            error_code = "ERR-EDITOR-ADVANCED-001"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            error_html = f"<div style='padding: 40px; text-align: center; color: #f87171;'><div style='font-size: 18px; margin-bottom: 12px;'>❌ Error Displaying Advanced Mode</div><div style='font-size: 13px;'>{str(e)}</div></div>"
            try:
                self._execute_js(f"document.getElementById('editor-content').innerHTML = {json.dumps(error_html)};")
            except:
                pass
    
    def _save_editor_file(self):
        """Save edited file with metadata changes"""
        if not hasattr(self, '_editor_instance') or not self._editor_instance:
            QMessageBox.warning(self, "Error", "No file loaded for editing!")
            return
        
        try:
            if hasattr(self, '_editor_file_path'):
                # Determine file type and set appropriate file filter
                file_type = getattr(self, '_editor_file_type', 'gguf')
                if file_type == '4dllm':
                    file_filter = "4DLLM Files (*.4dllm);;GGUF Files (*.gguf);;All Files (*)"
                else:
                    file_filter = "GGUF Files (*.gguf);;All Files (*)"
                
                output_path, _ = QFileDialog.getSaveFileName(
                    self, "Save File", str(self._editor_file_path),
                    file_filter
                )
                if output_path:
                    output_path = Path(output_path)
                    
                    # Get updated metadata if available
                    custom_metadata = None
                    if hasattr(self, '_editor_metadata') and self._editor_metadata:
                        custom_metadata = self._editor_metadata.copy()
                    
                    # Extract common fields from metadata
                    name = None
                    system_prompt = None
                    persona_prompt = None
                    
                    if custom_metadata:
                        name = custom_metadata.get('general.name') or custom_metadata.get('general.description')
                        system_prompt = custom_metadata.get('llama.system_prompt') or custom_metadata.get('chat_template.system_prompt')
                        persona_prompt = custom_metadata.get('llama.persona_prompt') or custom_metadata.get('chat_template.persona_prompt')
                        
                        # Remove fields that will be handled separately
                        custom_metadata = {k: v for k, v in custom_metadata.items() 
                                         if k not in ('general.name', 'general.description',
                                                    'llama.system_prompt', 'chat_template.system_prompt',
                                                    'llama.persona_prompt', 'chat_template.persona_prompt')}
                    
                    # If original file was a 4DLLM and output is also 4DLLM, rebuild the 4DLLM file
                    if file_type == '4dllm' and output_path.suffix.lower() == '.4dllm':
                        # Save the updated GGUF to temp file first
                        temp_gguf_path = getattr(self, '_editor_temp_gguf', None)
                        if not temp_gguf_path or not temp_gguf_path.exists():
                            QMessageBox.critical(self, "Error", "Temporary GGUF file not found! Cannot rebuild 4DLLM file.")
                            return
                        
                        # Save updated GGUF to temp file
                        if hasattr(self._editor_instance, 'save'):
                            import inspect
                            sig = inspect.signature(self._editor_instance.save)
                            if 'custom_metadata' in sig.parameters:
                                self._editor_instance.save(
                                    output_path=str(temp_gguf_path),
                                    name=str(name) if name else None,
                                    system_prompt=str(system_prompt) if system_prompt else None,
                                    persona_prompt=str(persona_prompt) if persona_prompt else None,
                                    custom_metadata=custom_metadata if custom_metadata else None
                                )
                            else:
                                self._editor_instance.save(str(temp_gguf_path))
                        
                        # Now rebuild the 4DLLM file with the updated GGUF
                        # Load original 4DLLM to get scripts and metadata
                        original_4dllm_path = self._editor_file_path
                        if runner_module:
                            try:
                                entries, toc_offset = runner_module.read_and_validate_toc(original_4dllm_path)
                                
                                # Extract scripts from original 4DLLM
                                script_items = []
                                for entry in entries:
                                    if entry.section_type == runner_module.SECTION_PYTHON_SCRIPT:
                                        section = runner_module.read_section_small(original_4dllm_path, entry)
                                        if section:
                                            script_data = runner_module.decode_and_validate(section)
                                            try:
                                                script_dict = json.loads(script_data.decode('utf-8'))
                                                script_name = script_dict.get('name', f'script_{len(script_items)}')
                                                script_content = script_dict.get('content', '')
                                                script_priority = script_dict.get('priority', 10)
                                                script_enabled = script_dict.get('enabled', True)
                                                
                                                # Use ScriptItem from builder_module
                                                script_item = builder_module.ScriptItem(
                                                    name=script_name,
                                                    content=script_content,
                                                    priority=script_priority,
                                                    enabled=script_enabled,
                                                    description=script_dict.get('description', ''),
                                                    source_path=script_dict.get('source_path', ''),
                                                    imports=script_dict.get('imports', [])
                                                )
                                                script_items.append(script_item)
                                            except Exception as e:
                                                DEBUG_LOGGER.warning(f"Failed to parse script section: {e}")
                                
                                # Create builder and rebuild 4DLLM
                                builder = builder_module.FourDLLMBuilderV2(str(temp_gguf_path))
                                
                                # Add scripts back
                                for script_item in script_items:
                                    builder.add_script(
                                        content=script_item.content,
                                        name=script_item.name,
                                        description=script_item.description,
                                        priority=script_item.priority,
                                        enabled=script_item.enabled,
                                        source_path=script_item.source_path,
                                        imports=script_item.imports
                                    )
                                
                                # Set metadata (including system prompt)
                                if system_prompt:
                                    builder.metadata['llama.system_prompt'] = system_prompt
                                    builder.metadata['chat_template.system_prompt'] = system_prompt
                                
                                # Add other custom metadata
                                if custom_metadata:
                                    for key, value in custom_metadata.items():
                                        builder.metadata[key] = value
                                
                                # Build the 4DLLM file
                                # build() method signature: build(output_path, compress_scripts=True, compress_metadata=True)
                                result_path = builder.build(str(output_path), compress_scripts=True, compress_metadata=True)
                                
                                QMessageBox.information(self, "Success", 
                                    f"4DLLM file saved successfully!\n\n"
                                    f"File: {output_path.name}\n"
                                    f"Scripts preserved: {len(script_items)}")
                            except Exception as e:
                                DEBUG_LOGGER.error(f"Error rebuilding 4DLLM file: {e}", exc_info=True)
                                QMessageBox.critical(self, "Error", 
                                    f"Failed to rebuild 4DLLM file: {e}\n\n"
                                    f"The GGUF file was saved, but the 4DLLM structure could not be rebuilt.")
                        else:
                            QMessageBox.critical(self, "Error", "Runner module not available! Cannot rebuild 4DLLM file.")
                    else:
                        # Regular GGUF save
                        if hasattr(self._editor_instance, 'save'):
                            import inspect
                            sig = inspect.signature(self._editor_instance.save)
                            if 'custom_metadata' in sig.parameters:
                                self._editor_instance.save(
                                    output_path=str(output_path),
                                    name=str(name) if name else None,
                                    system_prompt=str(system_prompt) if system_prompt else None,
                                    persona_prompt=str(persona_prompt) if persona_prompt else None,
                                    custom_metadata=custom_metadata if custom_metadata else None
                                )
                            else:
                                self._editor_instance.save(str(output_path))
                        
                        QMessageBox.information(self, "Success", "File saved successfully!")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error saving file: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to save file: {e}")
    
    def _create_backup(self):
        """Create backup of current file"""
        if not hasattr(self, '_editor_file_path') or not self._editor_file_path:
            QMessageBox.warning(self, "Error", "No file loaded!")
            return
        
        try:
            backup_path = self._editor_file_path.with_suffix(
                self._editor_file_path.suffix + f".backup_{int(time.time())}"
            )
            import shutil
            shutil.copy2(self._editor_file_path, backup_path)
            QMessageBox.information(self, "Success", f"Backup created: {backup_path.name}")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error creating backup: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to create backup: {e}")
    
    def _get_editor_metadata(self):
        """Get current editor metadata"""
        # Return metadata as JSON string to JavaScript
        if hasattr(self, '_editor_instance') and self._editor_instance:
            try:
                metadata = self._editor_instance.metadata if hasattr(self._editor_instance, 'metadata') else {}
                metadata_json = json.dumps(metadata)
                self._execute_js(f"window.editorMetadata = {metadata_json};")
            except Exception as e:
                DEBUG_LOGGER.error(f"Error getting metadata: {e}")
    
    def _refresh_editor_display(self):
        """Refresh the editor display with current metadata"""
        if not hasattr(self, '_editor_file_path') or not self._editor_file_path:
            return
        if not hasattr(self, '_editor_metadata'):
            self._editor_metadata = {}
        
        try:
            # Ensure metadata is a dict
            if not isinstance(self._editor_metadata, dict):
                DEBUG_LOGGER.warning(f"Editor metadata is not a dict: {type(self._editor_metadata)}, resetting")
                self._editor_metadata = {}
            
            # Reload metadata from editor instance if possible
            if hasattr(self, '_editor_instance') and self._editor_instance and hasattr(self._editor_instance, 'metadata'):
                instance_metadata = self._editor_instance.metadata
                if isinstance(instance_metadata, dict):
                    self._editor_metadata = instance_metadata
            
            # Get mode from JavaScript using execute_js with a callback approach
            # Since we can't get sync result, we'll use execute_js to store it in a hidden element
            # and then read it, or just default to easy mode for now
            # The mode should be stored when switchEditorMode is called
            mode = getattr(self, '_editor_mode', 'easy')
            
            # Use execute_js to get the mode and store it
            js_code = """
            (function() {
                var mode = window.editorMode || 'easy';
                // Store it in a data attribute on the editor content element
                var editorContent = document.getElementById('editor-content');
                if (editorContent) {
                    editorContent.setAttribute('data-editor-mode', mode);
                }
            })();
            """
            self._execute_js(js_code)
            
            # For now, use stored mode or default to easy
            # The mode is set in JavaScript via switchEditorMode
            # Redisplay with current metadata
            self._display_editor_metadata(self._editor_metadata, self._editor_file_path)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error refreshing editor display: {e}", exc_info=True)

    def _set_editor_mode(self, mode: str):
        """Set editor mode and refresh display."""
        try:
            if mode not in ("easy", "advanced"):
                return
            self._editor_mode = mode
            self._refresh_editor_display()
        except Exception as e:
            DEBUG_LOGGER.error(f"Error setting editor mode: {e}", exc_info=True)
    
    # ========== Trainer Methods ==========
    
    def _select_train_input_file(self):
        """Select input file for training (GGUF or 4DLLM)"""
        try:
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Select Model File for Training", "", 
                "Model Files (*.4dllm *.gguf);;4DLLM Files (*.4dllm);;GGUF Files (*.gguf);;All Files (*)"
            )
            if file_path:
                # Unload run model if one is loaded
                self._unload_other_models(exclude_section="train")
                
                self.train_input_file = Path(file_path)
                file_type = "4DLLM" if self.train_input_file.suffix == ".4dllm" else "GGUF"
                DEBUG_LOGGER.info(f"Selected training input file: {self.train_input_file} (Type: {file_type})")
                
                # Escape single quotes for JavaScript
                file_name_escaped = self.train_input_file.name.replace("'", "\\'")
                self._execute_js(f"document.getElementById('train-input-file').textContent = '{file_name_escaped} ({file_type})';")
                
                # Auto-generate output path
                if not self.train_output_file:
                    if self.train_input_file.suffix == ".4dllm":
                        output_path = self.train_input_file.parent / f"{self.train_input_file.stem}_trained.4dllm"
                    else:
                        output_path = self.train_input_file.parent / f"{self.train_input_file.stem}_trained.gguf"
                    self.train_output_file = output_path
                    output_name_escaped = output_path.name.replace("'", "\\'")
                    self._execute_js(f"document.getElementById('train-output-file').textContent = '{output_name_escaped}';")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error selecting training input file: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to select file: {e}")
    
    def _load_train_model(self):
        """Load and validate model file (4DLLM or GGUF) - handles errors gracefully"""
        if not self.train_input_file:
            QMessageBox.warning(self, "No File Selected", "Please select a model file first.")
            return
        
        if not self.train_input_file.exists():
            error_code = f"ERR-LOAD-{hash('file_not_found') % 10000:04d}"
            error_msg = f"File not found: {self.train_input_file}"
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
            self._show_model_status("error", f"❌ Error [{error_code}]: File not found")
            QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}")
            return
        
        file_suffix = self.train_input_file.suffix.lower()
        self._show_model_status("loading", "⏳ Loading model...")
        
        # Run validation in a thread to prevent UI freeze
        def validate_model():
            try:
                if file_suffix == ".4dllm":
                    self._validate_4dllm_model()
                elif file_suffix == ".gguf":
                    self._validate_gguf_model()
                else:
                    error_code = f"ERR-LOAD-{hash('unsupported_format') % 10000:04d}"
                    error_msg = f"Unsupported file format: {file_suffix}"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: Unsupported format"))
                    QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}"))
            except Exception as e:
                error_code = f"ERR-LOAD-{hash(str(e)) % 10000:04d}"
                error_msg = f"Error loading model: {e}"
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: {str(e)[:50]}"))
                QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}\n\nSee debug log for details."))
        
        import threading
        thread = threading.Thread(target=validate_model, daemon=True)
        thread.start()
    
    def _validate_4dllm_model(self):
        """Validate 4DLLM model file"""
        try:
            DEBUG_LOGGER.info(f"Validating 4DLLM file: {self.train_input_file}")
            
            # Check if runner_module is available
            if not runner_module:
                error_code = f"ERR-LOAD-{hash('runner_module_missing') % 10000:04d}"
                error_msg = "Runner module not available - cannot validate 4DLLM file"
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: Runner module missing"))
                QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}\n\nPlease ensure 4dllm_runner.py exists."))
                return
            
            # Read and validate TOC
            try:
                entries, toc_offset = read_and_validate_toc(self.train_input_file)
                DEBUG_LOGGER.info(f"4DLLM TOC validated: {len(entries)} sections found")
                
                # Check for GGUF section
                has_gguf = any(entry.section_type == SECTION_GGUF_DATA for entry in entries)
                if not has_gguf:
                    error_code = f"ERR-LOAD-{hash('no_gguf_section') % 10000:04d}"
                    error_msg = "4DLLM file does not contain GGUF data section"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: No GGUF section"))
                    QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}"))
                    return
                
                # Success
                file_size_mb = self.train_input_file.stat().st_size / (1024 * 1024)
                success_msg = f"✅ 4DLLM model loaded successfully\n{len(entries)} sections, {file_size_mb:.1f} MB"
                DEBUG_LOGGER.info(f"4DLLM model validated successfully: {len(entries)} sections")
                QTimer.singleShot(0, lambda: self._show_model_status("success", success_msg))
                
            except Exception as toc_error:
                error_code = f"ERR-LOAD-{hash(str(toc_error)) % 10000:04d}"
                error_msg = f"Failed to read 4DLLM TOC: {toc_error}"
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: TOC read failed"))
                QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}\n\nSee debug log for details."))
                
        except Exception as e:
            error_code = f"ERR-LOAD-{hash(str(e)) % 10000:04d}"
            error_msg = f"Error validating 4DLLM model: {e}"
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: Validation failed"))
            QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}"))
    
    def _validate_gguf_model(self):
        """Validate GGUF model file"""
        try:
            DEBUG_LOGGER.info(f"Validating GGUF file: {self.train_input_file}")
            
            # Check file magic (GGUF files start with "GGUF")
            with open(self.train_input_file, "rb") as f:
                magic = f.read(4)
                if magic != b"GGUF":
                    error_code = f"ERR-LOAD-{hash('invalid_gguf_magic') % 10000:04d}"
                    error_msg = f"Invalid GGUF file: magic mismatch (got {magic})"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: Invalid GGUF format"))
                    QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}"))
                    return
            
            # Success
            file_size_mb = self.train_input_file.stat().st_size / (1024 * 1024)
            success_msg = f"✅ GGUF model loaded successfully\n{file_size_mb:.1f} MB"
            DEBUG_LOGGER.info(f"GGUF model validated successfully: {file_size_mb:.1f} MB")
            QTimer.singleShot(0, lambda: self._show_model_status("success", success_msg))
            
        except Exception as e:
            error_code = f"ERR-LOAD-{hash(str(e)) % 10000:04d}"
            error_msg = f"Error validating GGUF model: {e}"
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            QTimer.singleShot(0, lambda: self._show_model_status("error", f"❌ Error [{error_code}]: Validation failed"))
            QTimer.singleShot(0, lambda: QMessageBox.critical(self, "Load Error", f"{error_msg}\n\nError Code: {error_code}"))
    
    def _show_model_status(self, status_type: str, message: str):
        """Show model loading status in UI"""
        try:
            status_el = "document.getElementById('train-model-status')"
            color_map = {
                "loading": "#06b6d4",  # Cyan
                "success": "#34d399",  # Green
                "error": "#f87171"     # Red
            }
            color = color_map.get(status_type, "#94a3b8")
            message_escaped = message.replace("'", "\\'").replace("\n", "\\n")
            self._execute_js(f"""
                {status_el}.style.display = 'block';
                {status_el}.style.color = '{color}';
                {status_el}.textContent = '{message_escaped}';
            """)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error showing model status: {e}")
    
    def _select_train_dataset(self):
        """Select training dataset file"""
        try:
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Select Training Dataset", "", 
                "Dataset Files (*.json *.jsonl *.txt);;All Files (*)"
            )
            if file_path:
                self.train_dataset_file = Path(file_path)
                file_size_kb = self.train_dataset_file.stat().st_size / 1024
                DEBUG_LOGGER.info(f"Selected training dataset: {self.train_dataset_file} ({file_size_kb:.1f} KB)")
                
                # Escape single quotes for JavaScript
                file_name_escaped = self.train_dataset_file.name.replace("'", "\\'")
                self._execute_js(f"document.getElementById('train-dataset-file').innerHTML = '{file_name_escaped}<br><span style=\\'font-size: 11px; color: #64748b;\\'>({file_size_kb:.1f} KB)</span>';")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error selecting training dataset: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to select dataset: {e}")
    
    def _select_train_output_file(self):
        """Select output path for trained model"""
        try:
            if not self.train_input_file:
                QMessageBox.warning(self, "Warning", "Please select an input file first.")
                return
            
            # Determine output format based on input
            if self.train_input_file.suffix == ".4dllm":
                default_path = self.train_input_file.parent / f"{self.train_input_file.stem}_trained.4dllm"
                file_filter = "4DLLM Files (*.4dllm);;GGUF Files (*.gguf);;All Files (*)"
            else:
                default_path = self.train_input_file.parent / f"{self.train_input_file.stem}_trained.gguf"
                file_filter = "GGUF Files (*.gguf);;4DLLM Files (*.4dllm);;All Files (*)"
            
            file_path, _ = QFileDialog.getSaveFileName(
                self, "Save Trained Model File", str(default_path),
                file_filter
            )
            if file_path:
                self.train_output_file = Path(file_path)
                DEBUG_LOGGER.info(f"Selected training output file: {self.train_output_file}")
                file_name_escaped = self.train_output_file.name.replace("'", "\\'")
                self._execute_js(f"document.getElementById('train-output-file').textContent = '{file_name_escaped}';")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error selecting training output file: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to select output file: {e}")
    
    def _load_train_preset(self, preset_name: str):
        """Load training preset parameters"""
        if preset_name in TRAINING_PRESETS:
            preset = TRAINING_PRESETS[preset_name]
            for key, value in preset.items():
                self._execute_js(f"document.getElementById('train-{key.replace('_', '-')}').value = '{value}';")
    
    def _start_training(self):
        """Start training process - NEVER CRASHES, shows error dialog on any error"""
        import traceback
        error_code = ""
        error_msg = ""
        traceback_str = ""
        
        try:
            DEBUG_LOGGER.info("="*80)
            DEBUG_LOGGER.info("_start_training() CALLED")
            DEBUG_LOGGER.info("="*80)
            
            DEBUG_LOGGER.info(f"train_input_file: {self.train_input_file}")
            DEBUG_LOGGER.info(f"train_dataset_file: {self.train_dataset_file}")
            DEBUG_LOGGER.info(f"train_output_file: {self.train_output_file}")
            DEBUG_LOGGER.info(f"is_training: {self.is_training}")
            
            # Validate input file
            try:
                if not self.train_input_file:
                    error_msg = "Please select an input model file (GGUF or 4DLLM) first."
                    error_code = "ERR-TRAIN-INPUT-001"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                
                DEBUG_LOGGER.info(f"Checking if input file exists: {self.train_input_file}")
                if not self.train_input_file.exists():
                    error_msg = f"Input file does not exist: {self.train_input_file}"
                    error_code = "ERR-TRAIN-INPUT-002"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Input file exists - OK")
            except Exception as e:
                error_msg = f"Error validating input file: {e}"
                error_code = "ERR-TRAIN-INPUT-003"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Validate dataset
            try:
                if not self.train_dataset_file:
                    error_msg = "Please select a training dataset file first."
                    error_code = "ERR-TRAIN-DATASET-001"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                
                DEBUG_LOGGER.info(f"Checking if dataset file exists: {self.train_dataset_file}")
                if not self.train_dataset_file.exists():
                    error_msg = f"Dataset file does not exist: {self.train_dataset_file}"
                    error_code = "ERR-TRAIN-DATASET-002"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Dataset file exists - OK")
            except Exception as e:
                error_msg = f"Error validating dataset file: {e}"
                error_code = "ERR-TRAIN-DATASET-003"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Validate output
            try:
                if not self.train_output_file:
                    error_msg = "Please select an output file path."
                    error_code = "ERR-TRAIN-OUTPUT-001"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Output file path set - OK")
            except Exception as e:
                error_msg = f"Error validating output file: {e}"
                error_code = "ERR-TRAIN-OUTPUT-002"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Check if already training
            try:
                if self.is_training:
                    error_msg = "Training is already in progress."
                    error_code = "ERR-TRAIN-STATUS-001"
                    DEBUG_LOGGER.warning(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Warning", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Not currently training - OK")
            except Exception as e:
                error_msg = f"Error checking training status: {e}"
                error_code = "ERR-TRAIN-STATUS-002"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Get training parameters from UI
            DEBUG_LOGGER.info("Reading training parameters from UI...")
            try:
                params = TRAINING_PRESETS["Standard Training"].copy()
                DEBUG_LOGGER.info(f"Using default params: {params}")
            except Exception as e:
                DEBUG_LOGGER.error(f"Error getting default params: {e}", exc_info=True)
                params = {
                    "epochs": "5",
                    "learning_rate": "0.0001",
                    "batch_size": "8",
                    "context_size": "4096",
                    "lora_r": "16",
                    "lora_alpha": "32",
                    "lora_dropout": "0.1"
                }
            
            DEBUG_LOGGER.info("Calling _get_training_params_and_start...")
            try:
                # Call directly without timer to avoid issues
                self._get_training_params_and_start(params)
            except Exception as e:
                error_msg = f"Error in _get_training_params_and_start: {e}"
                error_code = "ERR-TRAIN-START-001"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error("="*80)
                DEBUG_LOGGER.error(f"[{error_code}] CRITICAL ERROR calling _get_training_params_and_start")
                DEBUG_LOGGER.error(error_msg, exc_info=True)
                DEBUG_LOGGER.error(f"Full traceback:\n{traceback_str}")
                DEBUG_LOGGER.error("="*80)
                self.is_training = False
                try:
                    self._append_train_log(f"\n❌ [{error_code}] {error_msg}\n", "error")
                except:
                    pass
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
            
        except Exception as e:
            error_msg = f"Unexpected error starting training: {e}"
            error_code = "ERR-TRAIN-FATAL-001"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error("="*80)
            DEBUG_LOGGER.error(f"[{error_code}] CRITICAL ERROR in _start_training")
            DEBUG_LOGGER.error(error_msg, exc_info=True)
            DEBUG_LOGGER.error(f"Full traceback:\n{traceback_str}")
            DEBUG_LOGGER.error("="*80)
            self.is_training = False
            try:
                self._append_train_log(f"\n❌ [{error_code}] {error_msg}\n", "error")
            except:
                pass
            try:
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
            except:
                pass  # If even showing error dialog fails, just log it
    
    def _get_training_params_and_start(self, default_params: Dict[str, str]):
        """Get training parameters from JS and start training - NEVER CRASHES"""
        import traceback
        error_code = ""
        error_msg = ""
        traceback_str = ""
        
        try:
            DEBUG_LOGGER.info("="*80)
            DEBUG_LOGGER.info("_get_training_params_and_start() CALLED")
            DEBUG_LOGGER.info(f"default_params: {default_params}")
            
            # Capture file paths as strings BEFORE starting thread (thread-safe)
            try:
                DEBUG_LOGGER.info("Step 1: Validating file selections...")
                if not self.train_input_file:
                    error_msg = "No input file selected"
                    error_code = "ERR-TRAIN-PARAMS-001"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info(f"Input file: {self.train_input_file}")
                
                if not self.train_dataset_file:
                    error_msg = "No dataset file selected"
                    error_code = "ERR-TRAIN-PARAMS-002"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info(f"Dataset file: {self.train_dataset_file}")
                
                if not self.train_output_file:
                    error_msg = "No output file selected"
                    error_code = "ERR-TRAIN-PARAMS-003"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info(f"Output file: {self.train_output_file}")
            except Exception as e:
                error_msg = f"Error validating file selections: {e}"
                error_code = "ERR-TRAIN-PARAMS-004"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Convert to strings for thread safety
            try:
                DEBUG_LOGGER.info("Step 2: Converting paths to strings...")
                input_file_str = str(self.train_input_file)
                dataset_file_str = str(self.train_dataset_file)
                output_file_str = str(self.train_output_file)
                DEBUG_LOGGER.info(f"Input file string: {input_file_str}")
                DEBUG_LOGGER.info(f"Dataset file string: {dataset_file_str}")
                DEBUG_LOGGER.info(f"Output file string: {output_file_str}")
            except Exception as e:
                error_msg = f"Error converting paths to strings: {e}"
                error_code = "ERR-TRAIN-PARAMS-005"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Validate files exist before starting
            try:
                DEBUG_LOGGER.info("Step 3: Validating file existence...")
                input_path = Path(input_file_str)
                if not input_path.exists():
                    error_msg = f"Input file does not exist: {input_file_str}"
                    error_code = "ERR-TRAIN-PARAMS-006"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Input file exists - OK")
                
                dataset_path = Path(dataset_file_str)
                if not dataset_path.exists():
                    error_msg = f"Dataset file does not exist: {dataset_file_str}"
                    error_code = "ERR-TRAIN-PARAMS-007"
                    DEBUG_LOGGER.error(f"[{error_code}] {error_msg}")
                    QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, error_code))
                    return
                DEBUG_LOGGER.info("Dataset file exists - OK")
            except Exception as e:
                error_msg = f"Error validating file existence: {e}"
                error_code = "ERR-TRAIN-PARAMS-008"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Use default params for now
            try:
                DEBUG_LOGGER.info("Step 4: Preparing parameters...")
                params = default_params.copy()
                DEBUG_LOGGER.info(f"Using training parameters: {params}")
            except Exception as e:
                error_msg = f"Error preparing parameters: {e}"
                error_code = "ERR-TRAIN-PARAMS-009"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            # Start training in a thread
            try:
                DEBUG_LOGGER.info("Step 5: Setting training flag...")
                self.is_training = True
                DEBUG_LOGGER.info("is_training set to True")
            except Exception as e:
                error_msg = f"Error setting training flag: {e}"
                error_code = "ERR-TRAIN-PARAMS-010"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            try:
                DEBUG_LOGGER.info("Step 6: Updating UI...")
                # Update UI immediately - set progress to 1% to show it's starting
                # Wrap in IIFE to avoid variable redeclaration errors
                self._execute_js("""
                    (function() {
                        var startBtn = document.getElementById('train-start-btn');
                        var stopBtn = document.getElementById('train-stop-btn');
                        var stats = document.getElementById('train-stats');
                        var bar = document.getElementById('train-progress-bar');
                        var text = document.getElementById('train-progress-text');
                        if (startBtn) startBtn.style.display = 'none';
                        if (stopBtn) stopBtn.style.display = 'block';
                        if (stats) stats.innerHTML = '<div>Epoch: <span style="color: #06b6d4;">Starting...</span></div><div>Loss: <span style="color: #06b6d4;">-</span></div><div>Time: <span style="color: #06b6d4;">0s</span></div><div>Status: <span style="color: #34d399;">Training</span></div>';
                        if (bar) bar.style.width = '1%';
                        if (text) text.textContent = 'Initializing...';
                    })();
                """)
                DEBUG_LOGGER.info("UI updated - OK")
            except Exception as e:
                DEBUG_LOGGER.warning(f"Error updating UI (non-critical): {e}")
            
            try:
                DEBUG_LOGGER.info("Step 7: Clearing training log...")
                self._execute_js("document.getElementById('train-output').textContent = '';")
                self._append_train_log("Starting training process...\n", "info")
                DEBUG_LOGGER.info("Training log cleared - OK")
            except Exception as e:
                DEBUG_LOGGER.warning(f"Error clearing log (non-critical): {e}")
            
            try:
                DEBUG_LOGGER.info("Step 8: Logging file info...")
                input_path = Path(input_file_str)
                file_type = "4DLLM" if input_path.suffix.lower() == ".4dllm" else "GGUF"
                self._append_train_log(f"Input: {input_path.name} ({file_type})\n", "info")
                self._append_train_log(f"Dataset: {Path(dataset_file_str).name}\n", "info")
                self._append_train_log(f"Output: {Path(output_file_str).name}\n", "info")
                self._append_train_log(f"Parameters: {json.dumps(params, indent=2)}\n", "info")
                DEBUG_LOGGER.info("File info logged - OK")
            except Exception as e:
                DEBUG_LOGGER.warning(f"Error logging file info (non-critical): {e}")
            
            try:
                DEBUG_LOGGER.info("Step 9: Creating training thread...")
                self.training_thread = threading.Thread(
                    target=self._run_training,
                    args=(params, input_file_str, dataset_file_str, output_file_str),
                    daemon=True
                )
                DEBUG_LOGGER.info("Training thread created - OK")
            except Exception as e:
                error_msg = f"Error creating training thread: {e}"
                error_code = "ERR-TRAIN-PARAMS-011"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            try:
                DEBUG_LOGGER.info("Step 10: Starting training thread...")
                self.training_thread.start()
                DEBUG_LOGGER.info("Training thread started - OK")
                DEBUG_LOGGER.info("="*80)
            except Exception as e:
                error_msg = f"Error starting training thread: {e}"
                error_code = "ERR-TRAIN-PARAMS-012"
                traceback_str = traceback.format_exc()
                DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
                DEBUG_LOGGER.error(f"Traceback:\n{traceback_str}")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
        except Exception as e:
            error_msg = f"Unexpected error in training setup: {e}"
            error_code = "ERR-TRAIN-PARAMS-FATAL-001"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error("="*80)
            DEBUG_LOGGER.error(f"[{error_code}] CRITICAL ERROR in _get_training_params_and_start")
            DEBUG_LOGGER.error(error_msg, exc_info=True)
            DEBUG_LOGGER.error(f"Full traceback:\n{traceback_str}")
            DEBUG_LOGGER.error("="*80)
            self.is_training = False
            try:
                self._append_train_log(f"\n❌ [{error_code}] {error_msg}\n", "error")
            except:
                pass
            try:
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
            except:
                pass  # If even showing error dialog fails, just log it
    
    def _stop_training(self):
        """Stop training process"""
        DEBUG_LOGGER.info("="*80)
        DEBUG_LOGGER.info("_stop_training() CALLED")
        DEBUG_LOGGER.info(f"is_training: {self.is_training}")
        DEBUG_LOGGER.info("="*80)
        
        try:
            if not self.is_training:
                DEBUG_LOGGER.info("Not training - nothing to stop")
                return
            
            DEBUG_LOGGER.info("Setting is_training = False")
            self.is_training = False
            DEBUG_LOGGER.info(f"is_training set to: {self.is_training}")
            
            if self.training_process:
                try:
                    DEBUG_LOGGER.info("Terminating training process")
                    self.training_process.terminate()
                    DEBUG_LOGGER.info("Training process terminated")
                except Exception as e:
                    DEBUG_LOGGER.error(f"Error terminating process: {e}")
            
            DEBUG_LOGGER.info("Appending stop message to log")
            self._append_train_log("\n[Training stopped by user]\n", "warning")
            
            DEBUG_LOGGER.info("Updating UI to show stopped state")
            self._execute_js("""
                document.getElementById('train-start-btn').style.display = 'block';
                document.getElementById('train-stop-btn').style.display = 'none';
                document.getElementById('train-stats').innerHTML = '<div>Epoch: <span style="color: #64748b;">-</span></div><div>Loss: <span style="color: #64748b;">-</span></div><div>Time: <span style="color: #64748b;">-</span></div><div>Status: <span style="color: #64748b;">Stopped</span></div>';
                document.getElementById('train-progress-bar').style.width = '0%';
                document.getElementById('train-progress-text').textContent = 'Stopped';
            """)
            
            DEBUG_LOGGER.info("Stop training completed successfully")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error in _stop_training: {e}", exc_info=True)
            import traceback
            DEBUG_LOGGER.error(f"Traceback: {traceback.format_exc()}")
    
    def _run_training(self, params: Dict[str, str], input_file_str: str, dataset_file_str: str, output_file_str: str):
        """Run training process (in thread) - receives file paths as strings for thread safety"""
        # CRITICAL: Use direct file logging - NO Qt objects in thread!
        def thread_log(msg, level="INFO"):
            try:
                timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                log_line = f"{timestamp} [{level}] 4dllm_runner_gui_qt: {msg}\n"
                with open(DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
                    f.write(log_line)
                    f.flush()  # Force write immediately
                # Also print to stderr for immediate visibility
                print(f"[THREAD {level}] {msg}", file=sys.stderr, flush=True)
            except Exception as log_err:
                # Even logging can fail, so we silently continue
                print(f"[THREAD LOG ERROR] {log_err}", file=sys.stderr, flush=True)
        
        def safe_emit_signal(signal_func, *args):
            """Safely emit a signal, catching any errors"""
            try:
                signal_func(*args)
            except Exception as sig_err:
                thread_log(f"Error emitting signal: {sig_err}", "WARNING")
        
        def safe_reset_ui():
            """Safely reset UI on main thread"""
            try:
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
            except Exception as ui_err:
                thread_log(f"Error scheduling UI reset: {ui_err}", "WARNING")
        
        gguf_path = None
        import traceback
        
        # CRITICAL: Wrap EVERYTHING in try-except to prevent crashes
        try:
            thread_log("="*80)
            thread_log("_run_training() THREAD STARTED")
            thread_log("="*80)
            thread_log(f"params: {params}")
            thread_log(f"input_file_str: {input_file_str}")
            thread_log(f"dataset_file_str: {dataset_file_str}")
            thread_log(f"output_file_str: {output_file_str}")
            
            # Convert string paths to Path objects in thread
            try:
                thread_log("Converting string paths to Path objects...")
                train_input_file = Path(input_file_str)
                train_dataset_file = Path(dataset_file_str)
                train_output_file = Path(output_file_str)
                thread_log(f"train_input_file: {train_input_file}")
                thread_log(f"train_dataset_file: {train_dataset_file}")
                thread_log(f"train_output_file: {train_output_file}")
            except Exception as e:
                error_msg = f"Error converting paths: {e}"
                traceback_str = traceback.format_exc()
                thread_log(f"{error_msg}\n{traceback_str}", "ERROR")
                # Use signal to show error (thread-safe)
                error_code = "ERR-TRAIN-RUN-001"
                self.trainLogAppended.emit(f"❌ [{error_code}] {error_msg}\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                QTimer.singleShot(0, lambda: self._show_error_dialog("Training Error", error_msg, f"{error_code}\n\n{traceback_str}"))
                return
            
            thread_log(f"Input file: {train_input_file}")
            thread_log(f"Output file: {train_output_file}")
            thread_log(f"Dataset: {train_dataset_file}")
            thread_log(f"Parameters: {params}")
            
            # Check if runner_module is available - try to reload if missing
            # NOTE: We can't modify global variables from a thread safely, so we'll check locally
            # and handle the case where runner_module is None
            
            # Determine file type first
            file_suffix = train_input_file.suffix.lower()
            thread_log(f"File suffix: {file_suffix}")
            
            # Check runner_module availability (read-only check)
            runner_available = runner_module is not None
            thread_log(f"runner_module available: {runner_available}")
            
            # For 4DLLM files, try to extract GGUF (like original trainer)
            # If extraction fails, we'll show error but continue with simulation
            if file_suffix == ".4dllm" and not runner_available:
                # Show warning but continue with simulation (can't extract, but can still simulate)
                error_msg = "Warning: Cannot extract GGUF (runner module not available). Simulating training only."
                thread_log(error_msg, "WARNING")
                self.trainLogAppended.emit(f"⚠️ {error_msg}\n", "warning")
                self.trainLogAppended.emit("💡 Tip: Use a .gguf file directly for full training.\n", "info")
                # Continue anyway - use input file as placeholder
                gguf_path = train_input_file  # Use as placeholder
            
            # Validate files exist before proceeding
            if not train_input_file.exists():
                error_msg = f"Input file does not exist: {train_input_file}"
                thread_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                return
            
            if not train_dataset_file.exists():
                error_msg = f"Dataset file does not exist: {train_dataset_file}"
                thread_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                return
            
            # Get GGUF path based on file type (like original trainer)
            if file_suffix == ".4dllm":
                # Extract GGUF from 4DLLM (like original trainer)
                self.trainLogAppended.emit("Extracting GGUF model from 4DLLM file...\n", "info")
                thread_log("Extracting GGUF from 4DLLM file")
                
                # Update progress
                self.trainProgressUpdated.emit(2, 'Extracting GGUF from 4DLLM file...')
                
                try:
                    thread_log("Calling _extract_gguf_for_training...")
                    gguf_path = self._extract_gguf_for_training(train_input_file)
                    thread_log(f"_extract_gguf_for_training returned: {gguf_path}")
                    
                    if not gguf_path:
                        raise Exception("Failed to extract GGUF model")
                    
                    self.trainProgressUpdated.emit(5, 'GGUF extraction complete')
                    self.trainLogAppended.emit("✅ GGUF extracted successfully\n", "success")
                except Exception as extract_error:
                    error_msg = f"Exception during GGUF extraction: {extract_error}"
                    import traceback
                    traceback_str = traceback.format_exc()
                    thread_log("="*80, "ERROR")
                    thread_log("CRITICAL ERROR calling _extract_gguf_for_training", "ERROR")
                    thread_log(f"{error_msg}\n{traceback_str}", "ERROR")
                    thread_log("="*80, "ERROR")
                    
                    error_code = "ERR-TRAIN-RUN-002"
                    QTimer.singleShot(0, lambda: self._show_error_dialog(
                        "Training Error",
                        f"Exception occurred during GGUF extraction:\n\n{error_msg}",
                        f"{error_code}\n\n{traceback_str}"
                    ))
                    
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    self.trainLogAppended.emit("Check debug log for full error details.\n", "error")
                    self.is_training = False
                    QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                    return
                
                if not gguf_path:
                    error_msg = "Failed to extract GGUF from 4DLLM file (returned None)"
                    thread_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    self.trainLogAppended.emit("Check debug log for details.\n", "error")
                    self.is_training = False
                    QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                    return
                
                if not isinstance(gguf_path, Path):
                    error_msg = f"Invalid GGUF path type: {type(gguf_path)}"
                    thread_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    self.is_training = False
                    QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                    return
                
                try:
                    if not gguf_path.exists():
                        error_msg = f"Extracted GGUF file does not exist: {gguf_path}"
                        thread_log(error_msg, "ERROR")
                        self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                        self.is_training = False
                        QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                        return
                except Exception as check_error:
                    error_msg = f"Error checking GGUF file existence: {check_error}"
                    import traceback
                    thread_log(f"{error_msg}\n{traceback.format_exc()}", "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    self.is_training = False
                    QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                    return
                
                self.trainLogAppended.emit(f"✅ GGUF extracted: {gguf_path.name}\n", "success")
                try:
                    file_size = gguf_path.stat().st_size
                    thread_log(f"GGUF extracted successfully: {gguf_path} ({file_size} bytes)")
                except:
                    thread_log(f"GGUF extracted successfully: {gguf_path}")
            elif file_suffix == ".gguf":
                # Direct GGUF file
                gguf_path = train_input_file
                self.trainLogAppended.emit(f"Using GGUF file directly: {gguf_path.name}\n", "info")
                thread_log(f"Using GGUF file directly: {gguf_path}")
            else:
                error_msg = f"Unsupported file type: {file_suffix}"
                thread_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                self.trainLogAppended.emit("Supported formats: .4dllm, .gguf\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                return
            
            # Validate GGUF file exists
            if not gguf_path.exists():
                error_msg = f"GGUF file not found: {gguf_path}"
                thread_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                return
            
            # Validate dataset exists
            if not train_dataset_file.exists():
                error_msg = f"Dataset file not found: {train_dataset_file}"
                thread_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                self.is_training = False
                QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
                return
            
            # Get epochs safely
            try:
                epochs = int(params.get("epochs", "5"))
                if epochs < 1:
                    epochs = 5
                    thread_log("Epochs < 1, using default: 5", "WARNING")
            except (ValueError, TypeError):
                epochs = 5
                thread_log("Invalid epochs value, using default: 5", "WARNING")
            
            if not self.is_training:
                thread_log("Training stopped before simulation loop - exiting")
                safe_reset_ui()
                return

            # Start training simulation (like original trainer)
            thread_log("="*80)
            thread_log("STEP: Starting training simulation")
            thread_log(f"STEP: Epochs: {epochs}")
            thread_log(f"STEP: is_training flag: {self.is_training}")
            thread_log("="*80)
            
            self.trainLogAppended.emit(f"Training parameters: {params}\n", "info")
            thread_log("STEP: Emitted training parameters to UI")
            
            # Update initial progress - CRITICAL STEP
            thread_log("STEP: Updating initial progress to 10%")
            try:
                self.trainProgressUpdated.emit(10, 'Starting training...')
                thread_log("STEP: trainProgressUpdated signal emitted (10%)")
            except Exception as e:
                thread_log(f"ERROR emitting progress: {e}", "ERROR")
            
            try:
                stats_html = (
                    '<div>Epoch: <span style="color: #06b6d4;">0/' + str(epochs) + '</span></div>' +
                    '<div>Loss: <span style="color: #06b6d4;">-</span></div>' +
                    '<div>Time: <span style="color: #06b6d4;">0s</span></div>' +
                    '<div>Status: <span style="color: #34d399;">Training</span></div>'
                )
                self.trainStatsUpdated.emit(stats_html)
                thread_log("STEP: trainStatsUpdated signal emitted")
            except Exception as e:
                thread_log(f"ERROR emitting stats: {e}", "ERROR")
            
            # Also update via direct JS for reliability - MUST be on main thread!
            # Wrap in IIFE to avoid variable redeclaration errors
            try:
                thread_log("STEP: Scheduling UI update via direct JavaScript (main thread)")
                js_code = f"""
                    (function() {{
                        var bar = document.getElementById('train-progress-bar');
                        var text = document.getElementById('train-progress-text');
                        if (bar) {{
                            bar.style.width = '10%';
                        }}
                        if (text) {{
                            text.textContent = 'Starting training...';
                        }}
                        var stats = document.getElementById('train-stats');
                        if (stats) {{
                            stats.innerHTML = '<div>Epoch: <span style="color: #06b6d4;">0/{epochs}</span></div><div>Loss: <span style="color: #06b6d4;">-</span></div><div>Time: <span style="color: #06b6d4;">0s</span></div><div>Status: <span style="color: #34d399;">Training</span></div>';
                        }}
                    }})();
                """
                QTimer.singleShot(0, lambda: self._execute_js(js_code))
                thread_log("STEP: Direct JS update scheduled (will run on main thread)")
            except Exception as e:
                thread_log(f"ERROR scheduling JS update: {e}", "ERROR")
            
            start_time = time.time()
            thread_log(f"STEP: Training loop starting, start_time: {start_time}")
            thread_log(f"STEP: About to enter epoch loop, epochs={epochs}, is_training={self.is_training}")
            
            # Training loop (like original trainer)
            thread_log("="*80)
            thread_log("ENTERING EPOCH LOOP")
            thread_log("="*80)
            
            for epoch in range(1, epochs + 1):
                thread_log(f"EPOCH LOOP: Iteration {epoch}/{epochs}")
                thread_log(f"EPOCH LOOP: Checking is_training flag: {self.is_training}")
                
                if not self.is_training:
                    thread_log("EPOCH LOOP: Training stopped by user - breaking")
                    break
                
                thread_log(f"EPOCH LOOP: Processing epoch {epoch}")
                
                try:
                    thread_log(f"EPOCH {epoch}: Starting epoch processing")
                    
                    # Log epoch (like original trainer)
                    thread_log(f"EPOCH {epoch}: Emitting epoch log to UI")
                    self.trainLogAppended.emit(f"Epoch {epoch}/{epochs}...\n", "info")
                    thread_log(f"EPOCH {epoch}: Epoch log emitted")
                    
                    # Calculate progress (like original trainer)
                    elapsed = int(time.time() - start_time)
                    loss = max(0.01, 0.5 - (epoch * 0.05))
                    progress_percent = int((epoch / epochs) * 100)
                    thread_log(f"EPOCH {epoch}: Calculated progress - elapsed={elapsed}s, loss={loss:.4f}, progress={progress_percent}%")
                    
                    # Update stats (like original trainer)
                    thread_log(f"EPOCH {epoch}: Updating stats")
                    stats_html = (
                        f'<div>Epoch: <span style="color: #06b6d4;">{epoch}/{epochs}</span></div>' +
                        f'<div>Loss: <span style="color: #06b6d4;">{loss:.4f}</span></div>' +
                        f'<div>Time: <span style="color: #06b6d4;">{elapsed}s</span></div>' +
                        f'<div>Status: <span style="color: #34d399;">Training</span></div>'
                    )
                    self.trainStatsUpdated.emit(stats_html)
                    thread_log(f"EPOCH {epoch}: Stats updated signal emitted")
                    
                    self.trainProgressUpdated.emit(progress_percent, f'Epoch {epoch}/{epochs}')
                    thread_log(f"EPOCH {epoch}: Progress updated signal emitted ({progress_percent}%)")
                    
                    # Also update via direct JS - MUST be on main thread!
                    # Wrap in IIFE to avoid variable redeclaration errors
                    try:
                        thread_log(f"EPOCH {epoch}: Scheduling UI update via direct JS (main thread)")
                        js_code = f"""
                            (function() {{
                                var bar = document.getElementById('train-progress-bar');
                                var text = document.getElementById('train-progress-text');
                                var stats = document.getElementById('train-stats');
                                if (bar) {{
                                    bar.style.width = '{progress_percent}%';
                                }}
                                if (text) {{
                                    text.textContent = 'Epoch {epoch}/{epochs}';
                                }}
                                if (stats) {{
                                    stats.innerHTML = '<div>Epoch: <span style="color: #06b6d4;">{epoch}/{epochs}</span></div><div>Loss: <span style="color: #06b6d4;">{loss:.4f}</span></div><div>Time: <span style="color: #06b6d4;">{elapsed}s</span></div><div>Status: <span style="color: #34d399;">Training</span></div>';
                                }}
                            }})();
                        """
                        QTimer.singleShot(0, lambda: self._execute_js(js_code))
                        thread_log(f"EPOCH {epoch}: Direct JS update scheduled (will run on main thread)")
                    except Exception as js_err:
                        thread_log(f"EPOCH {epoch}: ERROR scheduling JS update: {js_err}", "ERROR")
                    
                    # Simulate training steps (like original trainer: 10 steps, 0.5s sleep)
                    thread_log(f"EPOCH {epoch}: Starting step loop (10 steps)")
                    for step in range(10):
                        thread_log(f"EPOCH {epoch} STEP {step+1}: Starting step")
                        
                        if not self.is_training:
                            thread_log(f"EPOCH {epoch} STEP {step+1}: Training stopped - returning")
                            return
                        
                        thread_log(f"EPOCH {epoch} STEP {step+1}: Sleeping 0.5s")
                        time.sleep(0.5)  # Like original trainer
                        thread_log(f"EPOCH {epoch} STEP {step+1}: Sleep complete")
                        
                        self.trainLogAppended.emit(f"  Step {step + 1}/10\n", "debug")
                        thread_log(f"EPOCH {epoch} STEP {step+1}: Step log emitted")
                        
                        # Update progress within epoch
                        epoch_progress = (step + 1) / 10
                        overall_progress = int(((epoch - 1 + epoch_progress) / epochs) * 100)
                        self.trainProgressUpdated.emit(overall_progress, f'Epoch {epoch}/{epochs} - Step {step + 1}/10')
                        thread_log(f"EPOCH {epoch} STEP {step+1}: Progress updated to {overall_progress}%")
                    
                    thread_log(f"EPOCH {epoch}: All steps completed")
                    thread_log(f"EPOCH {epoch}: Epoch {epoch}/{epochs} COMPLETE")
                    
                except Exception as epoch_error:
                    # Log error but continue - don't crash
                    error_code = f"ERR-EPOCH-{hash(str(epoch_error)) % 10000:04d}"
                    thread_log(f"Error in epoch {epoch} [Code: {error_code}]: {epoch_error}", "ERROR")
                    try:
                        safe_emit_signal(self.trainLogAppended.emit, f"⚠️ Error in epoch {epoch} [Code: {error_code}]: {epoch_error}\n", "warning")
                    except:
                        pass
                    continue  # Continue to next epoch
            
            # Training completed (like original trainer)
            thread_log("="*80)
            thread_log("CHECKING IF TRAINING COMPLETED")
            thread_log(f"is_training flag: {self.is_training}")
            thread_log("="*80)
            
            if self.is_training:
                thread_log("Training completed successfully - emitting completion messages")
                self.trainLogAppended.emit("=" * 60 + "\n", "info")
                self.trainLogAppended.emit("✅ Training completed successfully!\n", "success")
                
                # Save the trained model file
                try:
                    thread_log("Saving trained model file...")
                    output_path = Path(train_output_file)
                    
                    # For now, copy the input file as a placeholder (in real implementation, this would be the trained model)
                    if gguf_path and gguf_path.exists():
                        import shutil
                        thread_log(f"Copying model from {gguf_path} to {output_path}")
                        output_path.parent.mkdir(parents=True, exist_ok=True)
                        shutil.copy2(gguf_path, output_path)
                        file_size_mb = output_path.stat().st_size / (1024 * 1024)
                        thread_log(f"Model saved successfully: {output_path} ({file_size_mb:.2f} MB)")
                        self.trainLogAppended.emit(f"💾 Output saved to: {output_path.name} ({file_size_mb:.2f} MB)\n", "success")
                    else:
                        # Create a placeholder file if GGUF doesn't exist
                        thread_log("Creating placeholder output file")
                        output_path.parent.mkdir(parents=True, exist_ok=True)
                        output_path.touch()
                        self.trainLogAppended.emit(f"💾 Output file created: {output_path.name}\n", "info")
                        thread_log(f"Placeholder file created: {output_path}")
                except Exception as save_err:
                    error_msg = f"Error saving output file: {save_err}"
                    thread_log(f"ERROR: {error_msg}", "ERROR")
                    self.trainLogAppended.emit(f"⚠️ Warning: {error_msg}\n", "warning")
                
                self.trainLogAppended.emit("=" * 60 + "\n", "info")
                thread_log("Completion messages emitted to UI")
                
                # Update progress to 100% and reset UI
                self.trainProgressUpdated.emit(100, 'Training completed!')
                thread_log("Final progress update emitted (100%)")
                
                # Update UI to show completion
                try:
                    js_code = """
                        (function() {
                            var bar = document.getElementById('train-progress-bar');
                            var text = document.getElementById('train-progress-text');
                            var stats = document.getElementById('train-stats');
                            if (bar) bar.style.width = '100%';
                            if (text) text.textContent = 'Training completed!';
                            if (stats) {
                                stats.innerHTML = '<div>Epoch: <span style="color: #34d399;">Complete</span></div><div>Loss: <span style="color: #34d399;">-</span></div><div>Time: <span style="color: #34d399;">-</span></div><div>Status: <span style="color: #34d399;">Completed</span></div>';
                            }
                            // Show start button, hide stop button
                            var startBtn = document.getElementById('train-start-btn');
                            var stopBtn = document.getElementById('train-stop-btn');
                            if (startBtn) startBtn.style.display = 'block';
                            if (stopBtn) stopBtn.style.display = 'none';
                        })();
                    """
                    QTimer.singleShot(0, lambda: self._execute_js(js_code))
                    thread_log("UI completion update scheduled")
                except Exception as ui_err:
                    thread_log(f"ERROR scheduling UI completion update: {ui_err}", "ERROR")
                
                thread_log("Training simulation completed successfully")
                
                # Show success message (like original trainer) - MUST be on main thread
                try:
                    thread_log("Scheduling success message box")
                    output_file_str = str(train_output_file)  # Capture as string for lambda
                    QTimer.singleShot(100, lambda: self._show_training_success(output_file_str))
                    thread_log("Success message box scheduled")
                except Exception as e:
                    thread_log(f"ERROR scheduling success message: {e}", "ERROR")
            else:
                thread_log("Training was stopped - not showing completion message")
            
            self.is_training = False
            thread_log("Training process finished")
            
        except Exception as e:
            error_msg = f"Training error: {e}"
            import traceback
            traceback_str = traceback.format_exc()
            
            # Log directly to file (NO Qt objects!)
            try:
                with open(DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {'='*80}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: CRITICAL ERROR in _run_training thread\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {error_msg}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: Full traceback:\n{traceback_str}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {'='*80}\n")
            except:
                pass
            
            # Show error dialog via signal (thread-safe)
            error_code = "ERR-TRAIN-RUN-FATAL-001"
            full_error = f"Training crashed with error:\n\n{error_msg}\n\nError type: {type(e).__name__}\n\nCheck debug log for more details: {DEBUG_LOG_FILE}"
            QTimer.singleShot(0, lambda: self._show_error_dialog(
                "Training Failed",
                full_error,
                f"{error_code}\n\n{traceback_str}"
            ))
            
            self.trainLogAppended.emit(f"\n❌ {error_msg}\n", "error")
            self.trainLogAppended.emit("Check debug log for full error details.\n", "error")
            self.trainLogAppended.emit(f"Error type: {type(e).__name__}\n", "error")
            self.is_training = False
            # Reset UI on main thread
            QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
    
    def _reset_training_ui(self):
        """Reset training UI to initial state (thread-safe)"""
        try:
            # Use QTimer to ensure we're on the main thread
            QTimer.singleShot(0, lambda: self._reset_training_ui_safe())
        except Exception as e:
            DEBUG_LOGGER.error(f"Error scheduling training UI reset: {e}")
    
    def _reset_training_ui_safe(self):
        """Reset training UI (called on main thread)"""
        try:
            reset_stats = (
                '<div>Epoch: <span style="color: #64748b;">-</span></div>' +
                '<div>Loss: <span style="color: #64748b;">-</span></div>' +
                '<div>Time: <span style="color: #64748b;">-</span></div>' +
                '<div>Status: <span style="color: #64748b;">Ready</span></div>'
            )
            self.trainStatsUpdated.emit(reset_stats)
            self.trainProgressUpdated.emit(0, 'Not started')
            
            # Wrap in IIFE to avoid variable redeclaration errors
            self._execute_js("""
                (function() {
                    var startBtn = document.getElementById('train-start-btn');
                    var stopBtn = document.getElementById('train-stop-btn');
                    if (startBtn) startBtn.style.display = 'block';
                    if (stopBtn) stopBtn.style.display = 'none';
                })();
            """)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error resetting training UI: {e}", exc_info=True)
    
    def _show_training_success(self, output_file_path: str):
        """Show training success dialog (called on main thread)"""
        try:
            output_path = Path(output_file_path)
            if output_path.exists():
                file_size_mb = output_path.stat().st_size / (1024 * 1024)
                message = f"Training completed successfully!\n\nOutput saved to:\n{output_path}\n\nFile size: {file_size_mb:.2f} MB"
            else:
                message = f"Training completed successfully!\n\nOutput file: {output_path}\n\n(File may not exist yet)"
            
            msg = QMessageBox(self)
            msg.setIcon(QMessageBox.Icon.Information)
            msg.setWindowTitle("Training Complete")
            msg.setText("Training Complete ✅")
            msg.setInformativeText(message)
            msg.setStandardButtons(QMessageBox.StandardButton.Ok)
            msg.exec()
            self._reset_training_ui_safe()
        except Exception as e:
            DEBUG_LOGGER.error(f"Error showing training success dialog: {e}", exc_info=True)
            try:
                QMessageBox.information(self, "Training Complete", f"Training completed successfully!\n\nOutput: {output_file_path}")
                self._reset_training_ui_safe()
            except:
                pass
    
    def _show_error_popup(self, title: str, message: str):
        """Show error popup in a thread-safe way"""
        try:
            # Use QTimer to ensure we're on the main thread
            QTimer.singleShot(0, lambda: self._show_error_popup_safe(title, message))
        except Exception as e:
            DEBUG_LOGGER.error(f"Error scheduling error popup: {e}")
    
    def _show_error_popup_safe(self, title: str, message: str):
        """Show error popup (called on main thread)"""
        try:
            # Truncate message if too long for popup
            max_length = 2000
            if len(message) > max_length:
                message = message[:max_length] + f"\n\n... (truncated, see {DEBUG_LOG_FILE} for full error)"
            
            msg_box = QMessageBox(self)
            msg_box.setIcon(QMessageBox.Icon.Critical)
            msg_box.setWindowTitle(title)
            msg_box.setText(f"<b>{title}</b>")
            msg_box.setDetailedText(message)
            msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
            msg_box.exec()
        except Exception as e:
            DEBUG_LOGGER.error(f"Error showing error popup: {e}", exc_info=True)
            # Fallback: try simple message box
            try:
                QMessageBox.critical(self, title, message[:500] if len(message) > 500 else message)
            except:
                pass
    
    def _extract_gguf_for_training(self, input_file: Path) -> Optional[Path]:
        """Extract GGUF from 4DLLM file for training (based on working 4dllm_trainer.py)"""
        # Use direct file logging - NO Qt objects in thread!
        def extract_log(msg, level="INFO"):
            try:
                with open(DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [{level}] 4dllm_runner_gui_qt: {msg}\n")
            except:
                pass
        
        def cancelled() -> bool:
            if not self.is_training:
                extract_log("Extraction cancelled: is_training=False", "WARNING")
                return True
            return False
        
        try:
            extract_log("="*80)
            extract_log("_extract_gguf_for_training() CALLED")
            extract_log(f"Extracting GGUF from: {input_file}")
            if cancelled():
                return None
            
            if not runner_module:
                error_msg = "Runner module not available - cannot extract GGUF"
                extract_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            if not input_file or not input_file.exists():
                error_msg = f"Input file does not exist: {input_file}"
                extract_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            try:
                file_size = input_file.stat().st_size
                extract_log(f"File exists: True, size: {file_size} bytes")
            except Exception as e:
                error_msg = f"Error getting file size: {e}"
                import traceback
                extract_log(f"{error_msg}\n{traceback.format_exc()}", "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            # Validate file format first (like original trainer)
            self.trainLogAppended.emit("Validating 4DLLM file format...\n", "info")
            extract_log("Validating file magic and version...")
            
            FOURDLLM_MAGIC = getattr(runner_module, 'FOURDLLM_MAGIC', b'4DLM')
            FOURDLLM_VERSION_EXPECTED = getattr(runner_module, 'FOURDLLM_VERSION_EXPECTED', 2)
            
            with open(input_file, "rb") as f:
                magic = f.read(4)
                extract_log(f"File magic: {magic} (expected: {FOURDLLM_MAGIC})")
                
                if magic != FOURDLLM_MAGIC:
                    error_msg = f"Invalid 4DLLM file: magic mismatch. Got {magic}, expected {FOURDLLM_MAGIC}"
                    extract_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    return None
                
                if cancelled():
                    return None
                version = struct.unpack("<I", f.read(4))[0]
                extract_log(f"File version: {version} (expected: {FOURDLLM_VERSION_EXPECTED})")
                
                if version != FOURDLLM_VERSION_EXPECTED:
                    error_msg = f"Unsupported 4DLLM version: {version} (expected {FOURDLLM_VERSION_EXPECTED})"
                    extract_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    return None
            
            self.trainLogAppended.emit("Reading TOC from 4DLLM file...\n", "info")
            
            # Read TOC (like original trainer)
            entries = []
            toc_offset = 0
            try:
                extract_log("Calling read_and_validate_toc...")
                entries, toc_offset = read_and_validate_toc(input_file)
                extract_log(f"TOC read successfully: {len(entries)} entries, offset: {toc_offset}")
                
                if toc_offset == 0 and len(entries) == 0:
                    extract_log("TOC offset is 0 with 0 entries - likely TOC not found", "WARNING")
                    raise ValueError("TOC not found at offset 0")
                    
            except Exception as toc_error:
                import traceback
                extract_log(f"Failed to read TOC: {toc_error}\n{traceback.format_exc()}", "ERROR")
                # Try manual TOC search (like original trainer)
                self.trainLogAppended.emit("TOC read failed, attempting manual search...\n", "info")
                extract_log("Attempting manual TOC search...")
                
                search_bytes = min(64 * 1024 * 1024, file_size)
                extract_log(f"Searching last {search_bytes} bytes for TOC1 magic...")
                
                with open(input_file, "rb") as f:
                    f.seek(max(0, file_size - search_bytes))
                    data = f.read()
                    if cancelled():
                        return None
                    toc_pos = data.rfind(b"TOC1")
                    
                    if toc_pos >= 0:
                        actual_offset = file_size - search_bytes + toc_pos
                        extract_log(f"✅ Found TOC1 magic at offset: {actual_offset} (0x{actual_offset:x})")
                        self.trainLogAppended.emit(f"Found TOC1 at offset {actual_offset}\n", "info")
                        
                        # Read TOC manually
                        try:
                            with open(input_file, "rb") as f2:
                                f2.seek(actual_offset)
                                if cancelled():
                                    return None
                            TOC_MAGIC = getattr(runner_module, 'TOC_MAGIC', b'TOC1')
                            TOC_VERSION = getattr(runner_module, 'TOC_VERSION', 1)
                            TOC_HDR_STRUCT = getattr(runner_module, 'TOC_HDR_STRUCT', struct.Struct("<4sII"))
                            TOC_ENTRY_STRUCT = getattr(runner_module, 'TOC_ENTRY_STRUCT', struct.Struct("<BB2xQQQII"))
                            read_exact = getattr(runner_module, 'read_exact', None)
                            crc32_bytes = getattr(runner_module, 'crc32_bytes', None)
                            
                            if not read_exact or not crc32_bytes:
                                raise Exception("Required TOC reading functions not available")
                            
                            hdr = read_exact(f2, TOC_HDR_STRUCT.size)
                            magic, toc_ver, count = TOC_HDR_STRUCT.unpack(hdr)
                            
                            if magic != TOC_MAGIC:
                                raise ValueError(f"Bad TOC magic: {magic}")
                            if toc_ver != TOC_VERSION:
                                raise ValueError(f"Unsupported TOC version: {toc_ver}")
                            
                            extract_log(f"TOC header: version={toc_ver}, section_count={count}")
                            
                            if count == 0:
                                raise Exception("TOC has 0 sections")
                            
                            entries_bytes = read_exact(f2, TOC_ENTRY_STRUCT.size * count)
                            footer_crc = struct.unpack("<I", read_exact(f2, 4))[0]
                            
                            computed = crc32_bytes(hdr + entries_bytes)
                            if computed != footer_crc:
                                extract_log(f"TOC CRC mismatch: expected {footer_crc:08x}, got {computed:08x} (continuing)", "WARNING")
                            
                            # Parse entries using SimpleNamespace (like original)
                            from types import SimpleNamespace
                            entries = []
                            for i in range(count):
                                chunk = entries_bytes[i * TOC_ENTRY_STRUCT.size:(i + 1) * TOC_ENTRY_STRUCT.size]
                                stype, flags, off, size_c, size_u, extra_sz, crc_u = TOC_ENTRY_STRUCT.unpack(chunk)
                                
                                entry = SimpleNamespace()
                                entry.section_type = stype
                                entry.flags = flags
                                entry.offset = off
                                entry.size_c = size_c
                                entry.size_u = size_u
                                entry.extra_sz = extra_sz
                                entry.crc32_u = crc_u
                                
                                entries.append(entry)
                            
                            toc_offset = actual_offset
                            extract_log(f"✅ Successfully read {len(entries)} entries from TOC")
                            
                        except Exception as manual_error:
                            import traceback
                            extract_log(f"Manual TOC read failed: {manual_error}\n{traceback.format_exc()}", "ERROR")
                            self.trainLogAppended.emit(f"❌ Manual TOC read failed: {manual_error}\n", "error")
                            return None
                    else:
                        error_msg = "TOC1 magic not found in file"
                        extract_log(error_msg, "ERROR")
                        self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                        return None
            
            if not entries:
                error_msg = "No entries found in 4DLLM file"
                extract_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            # Try to get decode_and_validate function
            decode_and_validate = getattr(runner_module, 'decode_and_validate', None)
            extract_log(f"decode_and_validate available: {decode_and_validate is not None}")
            
            if len(entries) == 0:
                error_msg = "TOC contains 0 sections"
                extract_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            self.trainLogAppended.emit(f"Found {len(entries)} sections in TOC\n", "info")
            extract_log(f"Processing {len(entries)} sections")
            
            # Log all section types (like original)
            section_names = {0x01: "GGUF Data", 0x02: "Python Script", 0x03: "Metadata", 0x04: "Script Config", 0x05: "Manifest"}
            for i, entry in enumerate(entries):
                if cancelled():
                    return None
                sec_name = section_names.get(entry.section_type, f"Unknown ({entry.section_type})")
                extract_log(f"Section {i+1}: {sec_name} (type={entry.section_type}, size_u={entry.size_u}, offset={entry.offset})")
            
            # Find GGUF section (like original)
            gguf_entry = None
            for entry in entries:
                if cancelled():
                    return None
                extract_log(f"Checking entry: type={entry.section_type}, SECTION_GGUF_DATA={SECTION_GGUF_DATA}", "DEBUG")
                if entry.section_type == SECTION_GGUF_DATA:
                    gguf_entry = entry
                    extract_log(f"Found GGUF entry: size_u={entry.size_u}, size_c={entry.size_c}, offset={entry.offset}")
                    break
            
            if not gguf_entry:
                entry_types = [getattr(e, 'section_type', 'NO_ATTR') for e in entries]
                error_msg = f"No GGUF section found. Found {len(entries)} sections, but none with type {SECTION_GGUF_DATA}. Entry types: {entry_types}"
                extract_log(error_msg, "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            self.trainLogAppended.emit(f"Found GGUF section: {gguf_entry.size_u} bytes\n", "info")
            extract_log("Reading section data...")
            
            # Read section (like original - simpler approach)
            try:
                section = read_section_small(input_file, gguf_entry)
                extract_log("Section read, decoding and validating...")
            except Exception as read_error:
                error_msg = f"Error reading GGUF section: {read_error}"
                import traceback
                extract_log(f"{error_msg}\n{traceback.format_exc()}", "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            # Decode and validate (pass SectionSmall to runner module)
            self.trainLogAppended.emit("Decoding and validating section...\n", "info")
            try:
                decode_and_validate = getattr(runner_module, 'decode_and_validate', None)
                if not decode_and_validate:
                    error_msg = "decode_and_validate function not available"
                    extract_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    return None
                
                if cancelled():
                    return None
                data = decode_and_validate(section)
                if not data:
                    error_msg = "decode_and_validate returned None"
                    extract_log(error_msg, "ERROR")
                    self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                    return None
                
                self.trainLogAppended.emit(f"Decoded {len(data)} bytes of GGUF data\n", "info")
                extract_log(f"Decoded {len(data)} bytes of GGUF data")
            except Exception as decode_error:
                error_msg = f"Error decoding section: {decode_error}"
                import traceback
                extract_log(f"{error_msg}\n{traceback.format_exc()}", "ERROR")
                self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
                return None
            
            # Save to temp file (like original)
            temp_dir = Path(__file__).parent / "temp"
            temp_dir.mkdir(exist_ok=True)
            gguf_path = temp_dir / f"{input_file.stem}_extracted.gguf"
            
            self.trainLogAppended.emit(f"Writing GGUF to temporary file...\n", "info")
            extract_log(f"Writing GGUF to: {gguf_path}")
            
            with open(gguf_path, "wb") as f:
                f.write(data)
            
            self.trainLogAppended.emit(f"✅ Successfully extracted GGUF to: {gguf_path.name}\n", "success")
            extract_log(f"✅ GGUF extraction successful: {gguf_path}")
            return gguf_path
                
        except Exception as e:
            error_msg = f"Unexpected error extracting GGUF: {e}"
            import traceback
            traceback_str = traceback.format_exc()
            
            # Log directly to file (NO Qt objects!)
            try:
                with open(DEBUG_LOG_FILE, 'a', encoding='utf-8') as f:
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {'='*80}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: CRITICAL ERROR in _extract_gguf_for_training\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {error_msg}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: Full traceback:\n{traceback_str}\n")
                    f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 4dllm_runner_gui_qt: {'='*80}\n")
            except:
                pass
            
            # Show error dialog via signal (thread-safe)
            error_code = "ERR-TRAIN-EXTRACT-001"
            full_error = f"Failed to extract GGUF from 4DLLM file:\n\n{error_msg}\n\nError type: {type(e).__name__}\n\nCheck debug log for more details: {DEBUG_LOG_FILE}"
            QTimer.singleShot(0, lambda: self._show_error_dialog(
                "GGUF Extraction Failed",
                full_error,
                f"{error_code}\n\n{traceback_str}"
            ))
            
            self.trainLogAppended.emit(f"❌ {error_msg}\n", "error")
            self.trainLogAppended.emit("Check debug log for full error details.\n", "error")
            return None
    
    def _append_train_log(self, text: str, tag: str = "output"):
        """Append text to training log (thread-safe via signal)"""
        # Emit signal for thread-safe UI update
        self.trainLogAppended.emit(text, tag)
    
    def _append_train_log_safe(self, text: str, tag: str = "output"):
        """Append text to training log (called on main thread via signal)"""
        try:
            # Escape text for JavaScript
            text_escaped = json.dumps(text)
            self._execute_js(f"""
                (function() {{
                    var output = document.getElementById('train-output');
                    if (output) {{
                        var span = document.createElement('span');
                        span.className = '{tag}';
                        span.textContent = {text_escaped};
                        output.appendChild(span);
                        output.scrollTop = output.scrollHeight;
                    }}
                }})();
            """)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error appending to training log: {e}")
            # Fallback: try simple text append
            try:
                text_safe = text.replace("'", "\\'").replace("\n", "\\n")
                self._execute_js(f"document.getElementById('train-output').textContent += '{text_safe}';")
            except:
                pass
    
    def _update_train_stats_safe(self, stats_html: str):
        """Update training stats (called on main thread)"""
        try:
            # Use JSON encoding for proper escaping
            stats_escaped = json.dumps(stats_html)
            # Wrap in IIFE to avoid variable redeclaration errors
            self._execute_js(f"""
                (function() {{
                    var statsEl = document.getElementById('train-stats');
                    if (statsEl) {{
                        statsEl.innerHTML = {stats_escaped};
                    }}
                }})();
            """)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error updating training stats: {e}", exc_info=True)
            # Fallback: try simple replacement
            try:
                stats_safe = stats_html.replace("'", "\\'").replace("\n", "\\n").replace('"', '\\"')
                self._execute_js(f"""
                    (function() {{
                        var statsEl = document.getElementById('train-stats');
                        if (statsEl) statsEl.innerHTML = '{stats_safe}';
                    }})();
                """)
            except:
                pass
    
    def _update_train_progress_safe(self, percent: int, text: str):
        """Update training progress (called on main thread)"""
        try:
            # Use JSON encoding for proper escaping
            text_escaped = json.dumps(text)
            # Wrap in IIFE to avoid variable redeclaration errors
            self._execute_js(f"""
                (function() {{
                    var bar = document.getElementById('train-progress-bar');
                    var text = document.getElementById('train-progress-text');
                    if (bar) bar.style.width = '{percent}%';
                    if (text) text.textContent = {text_escaped};
                }})();
            """)
        except Exception as e:
            DEBUG_LOGGER.error(f"Error updating training progress: {e}", exc_info=True)
            # Fallback: try simple replacement
            try:
                text_safe = text.replace("'", "\\'")
                self._execute_js(f"""
                    (function() {{
                        var bar = document.getElementById('train-progress-bar');
                        var textEl = document.getElementById('train-progress-text');
                        if (bar) bar.style.width = '{percent}%';
                        if (textEl) textEl.textContent = '{text_safe}';
                    }})();
                """)
            except:
                pass
    
    def _execute_js_sync(self, js_code: str) -> str:
        """Execute JavaScript synchronously (limited support)"""
        # This is a simplified version - full sync execution would need more complex handling
        result = [None]
        def callback(value):
            result[0] = value
        # For now, return empty string - async execution is used instead
        return ""
    
    def _update_editor_metadata(self, key: str, value: str):
        """Update a metadata key-value pair"""
        if hasattr(self, '_editor_instance') and self._editor_instance:
            try:
                if hasattr(self._editor_instance, 'metadata'):
                    # Try to parse value as appropriate type
                    try:
                        value = json.loads(value)
                    except:
                        pass
                    self._editor_instance.metadata[key] = value
                    self._append_terminal(f"Updated metadata: {key} = {value}\n", "info")
            except Exception as e:
                DEBUG_LOGGER.error(f"Error updating metadata: {e}")
    
    def _advanced_refresh_metadata(self):
        """Refresh metadata from file"""
        if not hasattr(self, '_editor_instance') or not self._editor_instance:
            QMessageBox.warning(self, "Error", "No file loaded!")
            return
        
        try:
            # Reload metadata
            if hasattr(self._editor_instance, 'load'):
                metadata = self._editor_instance.load()
                self._editor_metadata = metadata
                if hasattr(self._editor_instance, 'metadata'):
                    self._editor_instance.metadata = metadata
                # Refresh display
                self._refresh_editor_display()
                QMessageBox.information(self, "Success", "Metadata refreshed successfully!")
            else:
                error_msg = "Editor instance doesn't support reloading"
                self._show_error_dialog("Editor Error", error_msg, "ERR-EDITOR-RELOAD-001")
        except Exception as e:
            import traceback
            error_msg = f"Failed to refresh metadata: {e}"
            error_code = "ERR-EDITOR-RELOAD-002"
            traceback_str = traceback.format_exc()
            DEBUG_LOGGER.error(f"[{error_code}] {error_msg}", exc_info=True)
            self._show_error_dialog("Editor Error", error_msg, f"{error_code}\n\n{traceback_str}")
    
    def _advanced_add_key(self):
        """Add a new metadata key - uses dialog"""
        if not HAS_PYQT6:
            return
        
        key, ok = QInputDialog.getText(self, "Add Metadata Key", "Enter new metadata key:")
        if ok and key.strip():
            key = key.strip()
            if not hasattr(self, '_editor_metadata'):
                self._editor_metadata = {}
            # Add with empty string value by default
            self._editor_metadata[key] = ""
            if hasattr(self, '_editor_instance') and self._editor_instance:
                if hasattr(self._editor_instance, 'metadata'):
                    self._editor_instance.metadata[key] = ""
            # Refresh display
            self._refresh_editor_display()
            QMessageBox.information(self, "Success", f"Added key: {key}")
    
    def _advanced_delete_key(self):
        """Delete selected metadata key - uses JavaScript to get selection"""
        # We need to get the selected key from JavaScript
        # For now, use a dialog
        if not HAS_PYQT6:
            return
        
        if not hasattr(self, '_editor_metadata') or not self._editor_metadata:
            QMessageBox.warning(self, "Error", "No metadata loaded!")
            return
        
        keys = sorted(self._editor_metadata.keys())
        key, ok = QInputDialog.getItem(self, "Delete Metadata Key", "Select key to delete:", keys, 0, False)
        if ok and key:
            del self._editor_metadata[key]
            if hasattr(self, '_editor_instance') and self._editor_instance:
                if hasattr(self._editor_instance, 'metadata') and key in self._editor_instance.metadata:
                    del self._editor_instance.metadata[key]
            # Refresh display
            self._refresh_editor_display()
            QMessageBox.information(self, "Success", f"Deleted key: {key}")
    
    def _advanced_save_item(self, key: str, value_text: str, value_type: str):
        """Save a metadata item with type conversion"""
        if not hasattr(self, '_editor_metadata'):
            self._editor_metadata = {}
        
        try:
            # Convert value based on type
            if value_type == 'int':
                value = int(value_text)
            elif value_type == 'float':
                value = float(value_text)
            elif value_type == 'bool':
                value = value_text.lower() in ('true', '1', 'yes', 'on')
            elif value_type == 'array':
                value = json.loads(value_text)
            else:
                value = value_text
            
            # Update metadata
            self._editor_metadata[key] = value
            if hasattr(self, '_editor_instance') and self._editor_instance:
                if hasattr(self._editor_instance, 'metadata'):
                    self._editor_instance.metadata[key] = value
            
            # Refresh display
            self._refresh_editor_display()
            QMessageBox.information(self, "Success", f"Saved: {key} = {value}")
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "Error", f"Invalid JSON for array type: {e}")
        except ValueError as e:
            QMessageBox.critical(self, "Error", f"Invalid value for {value_type}: {e}")
        except Exception as e:
            DEBUG_LOGGER.error(f"Error saving item: {e}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to save item: {e}")
    
    def _load_scripts_file(self):
        """Load a 4DLLM file and display embedded scripts."""
        try:
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Select 4DLLM File (Scripts)", "",
                "4DLLM Files (*.4dllm);;All Files (*)"
            )
            if file_path:
                self._scripts_file_path = Path(file_path)
                self._render_scripts_from_file(self._scripts_file_path)
        except Exception as e:
            DEBUG_LOGGER.error(f"[SCRIPTS] Error loading scripts file: {e}", exc_info=True)
            QMessageBox.critical(self, "Scripts Error", f"Failed to load scripts file: {e}")

    def _refresh_scripts(self):
        """Refresh scripts view for the last loaded 4DLLM file."""
        if hasattr(self, "_scripts_file_path") and self._scripts_file_path:
            self._render_scripts_from_file(self._scripts_file_path)
        else:
            QMessageBox.warning(self, "No File", "Please load a 4DLLM file first.")

    def _render_scripts_from_file(self, file_path: Path):
        """Parse script sections from 4DLLM and render them in the Scripts tab."""
        global runner_module
        try:
            file_name_escaped = html.escape(file_path.name)
            self._execute_js_safe(
                f"document.getElementById('scripts-file-info').textContent = 'File: {file_name_escaped}';"
            )
            self._execute_js_safe(
                "document.getElementById('scripts-output').innerHTML = '<span class=\"info\">⏳ Loading scripts...</span>';"
            )

            if not runner_module:
                self._execute_js_safe(
                    "document.getElementById('scripts-output').innerHTML = '<span class=\"error\">❌ Runner module not available.</span>';"
                )
                return

            entries, _toc_offset = runner_module.read_and_validate_toc(file_path)
            script_sections = [e for e in entries if e.section_type == runner_module.SECTION_PYTHON_SCRIPT]

            lines = []
            lines.append("<div style='color: #06b6d4; font-weight: 600; margin-bottom: 12px;'>=== 4DLLM Scripts ===</div>")
            lines.append(f"<div style='margin-bottom: 8px;'><strong>File:</strong> {file_name_escaped}</div>")
            lines.append(f"<div style='margin-bottom: 16px;'><strong>Script Sections:</strong> {len(script_sections)}</div>")
            lines.append(
                "<div style='margin-bottom: 16px; color: #94a3b8; font-size: 11px;'>"
                "Details below include name, priority, enabled state, imports, source, and code preview."
                "</div>"
            )

            if not script_sections:
                lines.append("<div style='color: #94a3b8;'>No script sections found in this file.</div>")
                self._execute_js_safe(
                    f"document.getElementById('scripts-output').innerHTML = {json.dumps(''.join(lines))};"
                )
                return

            for idx, entry in enumerate(script_sections):
                try:
                    section = runner_module.read_section_small(file_path, entry)
                    if not section:
                        continue
                    script_data = runner_module.decode_and_validate(section)
                    script_text = script_data.decode("utf-8", errors="replace")
                    if not script_text.strip():
                        script_dict = {
                            "name": f"script_{idx + 1}",
                            "content": "",
                            "priority": 10,
                            "enabled": True,
                            "description": "Empty script section",
                            "source_path": "",
                            "imports": [],
                        }
                    else:
                        try:
                            script_dict = json.loads(script_text)
                        except Exception:
                            # Legacy/raw script section (non-JSON)
                            script_dict = {
                                "name": f"script_{idx + 1}",
                                "content": script_text,
                                "priority": 10,
                                "enabled": True,
                                "description": "Legacy/raw script section (non-JSON)",
                                "source_path": "",
                                "imports": [],
                            }

                    name = str(script_dict.get("name", f"script_{idx}"))
                    priority = script_dict.get("priority", 10)
                    enabled = script_dict.get("enabled", True)
                    imports = script_dict.get("imports", [])
                    description = script_dict.get("description", "")
                    source_path = script_dict.get("source_path", "")
                    content = script_dict.get("content", "")

                    content_preview = content if len(content) <= 8000 else content[:8000] + "\n...[truncated]"
                    search_blob = f"{name} {description} {source_path} " + " ".join(imports)
                    size_c = entry.size_c if hasattr(entry, "size_c") else 0
                    size_u = entry.size_u if hasattr(entry, "size_u") else 0
                    content_len = len(content) if isinstance(content, str) else 0

                    lines.append(f"""
                        <div class='script-card' data-search='{html.escape(search_blob)}' style='background: rgba(30, 41, 59, 0.6); border: 1px solid rgba(71, 85, 105, 0.4); border-radius: 10px; padding: 12px; margin-bottom: 10px;'>
                            <div style='display: flex; justify-content: space-between; gap: 8px; align-items: center; margin-bottom: 8px;'>
                                <div style='font-weight: 600; color: #e2e8f0;'>🐍 {html.escape(name)}</div>
                                <div style='font-size: 11px; color: { "#34d399" if enabled else "#fbbf24" }; background: rgba(71, 85, 105, 0.25); padding: 2px 6px; border-radius: 6px;'>
                                    { "Enabled" if enabled else "Disabled" } · Priority {priority}
                                </div>
                            </div>
                            <div style='font-size: 11px; color: #94a3b8; margin-bottom: 6px;'>{html.escape(description) if description else "No description"}</div>
                            <div style='font-size: 11px; color: #64748b; margin-bottom: 8px;'>Source: {html.escape(source_path) if source_path else "Embedded"}</div>
                            <div style='display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px;'>
                                <span style='font-size: 10px; color: #06b6d4; background: rgba(6, 182, 212, 0.15); padding: 2px 6px; border-radius: 6px;'>Imports: {len(imports)}</span>
                                <span style='font-size: 10px; color: #94a3b8; background: rgba(71, 85, 105, 0.2); padding: 2px 6px; border-radius: 6px;'>Content: {content_len} chars</span>
                                <span style='font-size: 10px; color: #94a3b8; background: rgba(71, 85, 105, 0.2); padding: 2px 6px; border-radius: 6px;'>Section size: {size_u if size_u else size_c} bytes</span>
                                <span style='font-size: 10px; color: #94a3b8; background: rgba(71, 85, 105, 0.2); padding: 2px 6px; border-radius: 6px;'>Section #{idx + 1}</span>
                            </div>
                            {f"<div style='margin-bottom: 8px; color: #22d3ee; font-size: 11px;'>Imports: {', '.join([html.escape(x) for x in imports[:8]])}{' ...' if len(imports) > 8 else ''}</div>" if imports else "<div style='margin-bottom: 8px; color: #64748b; font-size: 11px;'>No imports detected</div>"}
                            <details style='margin-top: 6px;'>
                                <summary style='cursor: pointer; color: #22d3ee; font-size: 12px;'>View script code</summary>
                                <pre style='white-space: pre-wrap; word-break: break-word; margin-top: 8px; background: rgba(15, 23, 42, 0.7); padding: 10px; border-radius: 8px; font-size: 11px; color: #cbd5e1;'>{html.escape(content_preview)}</pre>
                            </details>
                        </div>
                    """)
                except Exception as script_err:
                    DEBUG_LOGGER.warning(f"[SCRIPTS] Failed to parse script section: {script_err}")
                    try:
                        lines.append(f"""
                            <div class='script-card' style='background: rgba(30, 41, 59, 0.6); border: 1px solid rgba(239, 68, 68, 0.4); border-radius: 10px; padding: 12px; margin-bottom: 10px;'>
                                <div style='font-weight: 600; color: #f87171; margin-bottom: 6px;'>⚠️ Script Section #{idx + 1} - Parse Error</div>
                                <div style='font-size: 11px; color: #94a3b8;'>Could not parse script JSON. This section may be corrupt or use an unsupported format.</div>
                                <div style='font-size: 11px; color: #64748b; margin-top: 6px;'>Error: {html.escape(str(script_err))}</div>
                            </div>
                        """)
                    except Exception:
                        pass

            html_content = "".join(lines)
            self._execute_js_safe(f"document.getElementById('scripts-output').innerHTML = {json.dumps(html_content)};")
        except Exception as e:
            DEBUG_LOGGER.error(f"[SCRIPTS] Error rendering scripts: {e}", exc_info=True)
            self._execute_js_safe(
                f"document.getElementById('scripts-output').innerHTML = '<span class=\"error\">❌ Error loading scripts: {html.escape(str(e))}</span>';"
            )

    def _inspect_file(self):
        """Load a file for inspection (.gguf or .4dllm) - SELF-HEALING: Never crashes"""
        # OUTER TRY-EXCEPT: Catch absolutely everything
        try:
            import threading
            main_thread_id = threading.current_thread().ident
            DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_file STARTED (main thread: {main_thread_id}) ==========")
            
            # INNER TRY-EXCEPT: File dialog and setup
            try:
                DEBUG_LOGGER.debug(f"[INSPECT] Opening file dialog...")
                try:
                    file_path, _ = QFileDialog.getOpenFileName(
                        self, "Load File for Inspection", "", 
                        "All Supported (*.gguf *.4dllm);;GGUF Files (*.gguf);;4DLLM Files (*.4dllm);;All Files (*)"
                    )
                except Exception as dialog_error:
                    DEBUG_LOGGER.error(f"[INSPECT] File dialog error: {dialog_error}", exc_info=True)
                    self._show_inspect_error_dialog(dialog_error, "Opening file dialog")
                    return
                
                DEBUG_LOGGER.debug(f"[INSPECT] File dialog returned: {file_path}")
                
                if not file_path:
                    DEBUG_LOGGER.info(f"[INSPECT] User cancelled file selection")
                    return
                
                # Safe path operations
                try:
                    file_path_obj = Path(file_path)
                    self._inspect_file_path = file_path_obj
                except Exception as path_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Path creation error: {path_error}", exc_info=True)
                    self._show_inspect_error_dialog(path_error, "Creating file path object")
                    return
                
                DEBUG_LOGGER.info(f"[INSPECT] Selected file: {file_path_obj}")
                DEBUG_LOGGER.debug(f"[INSPECT] File path object: {file_path_obj}")
                
                # Safe file existence check
                try:
                    file_exists = file_path_obj.exists()
                    DEBUG_LOGGER.debug(f"[INSPECT] File exists: {file_exists}")
                    if file_exists:
                        try:
                            file_size = file_path_obj.stat().st_size
                            DEBUG_LOGGER.debug(f"[INSPECT] File size: {file_size} bytes ({file_size / (1024*1024):.2f} MB)")
                        except Exception:
                            pass  # File size check failed, continue anyway
                except Exception:
                    pass  # File existence check failed, continue anyway
                
                DEBUG_LOGGER.debug(f"[INSPECT] File suffix: {file_path_obj.suffix}")
                
                # Show loading message (wrapped in try-except)
                try:
                    file_name_escaped = json.dumps(file_path_obj.name)
                    DEBUG_LOGGER.debug(f"[INSPECT] Showing loading message in UI...")
                    self._execute_js(f"""
                        const output = document.getElementById('inspect-output');
                        if (output) {{
                            output.innerHTML = '<span class="info">⏳ Loading file: ' + {file_name_escaped} + '...<br>Please wait, this may take a moment for large files.</span>';
                        }}
                    """)
                    DEBUG_LOGGER.debug(f"[INSPECT] Loading message displayed successfully")
                except Exception as e:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to show loading message: {e}", exc_info=True)
                    # Continue anyway - not critical
                
                # Perform inspection on main thread using QTimer to avoid Qt object access from background threads
                # Note: For very large files, this will block the UI, but it's necessary to avoid crashes
                def perform_inspection():
                    """Perform inspection on main thread - catches all exceptions"""
                    try:
                        DEBUG_LOGGER.info(f"[INSPECT] ========== perform_inspection STARTED ==========")
                        DEBUG_LOGGER.info(f"[INSPECT] Inspecting file: {file_path_obj}")
                        DEBUG_LOGGER.debug(f"[INSPECT] File path: {file_path_obj}")
                        
                        # Safe suffix check
                        try:
                            suffix_lower = file_path_obj.suffix.lower()
                            DEBUG_LOGGER.debug(f"[INSPECT] File suffix (lowercase): {suffix_lower}")
                        except Exception as suffix_error:
                            DEBUG_LOGGER.error(f"[INSPECT] Error getting suffix: {suffix_error}", exc_info=True)
                            suffix_lower = ""
                        
                        # Route to appropriate inspection method
                        try:
                            if suffix_lower == '.4dllm':
                                DEBUG_LOGGER.info(f"[INSPECT] Detected 4DLLM file, calling _inspect_4dllm_file...")
                                self._inspect_4dllm_file(file_path_obj)
                            elif suffix_lower == '.gguf':
                                DEBUG_LOGGER.info(f"[INSPECT] Detected GGUF file, calling _inspect_gguf_file...")
                                self._inspect_gguf_file(file_path_obj)
                            else:
                                DEBUG_LOGGER.warning(f"[INSPECT] Unsupported file type: {file_path_obj.suffix}")
                                try:
                                    suffix_escaped = json.dumps(file_path_obj.suffix)
                                    self._execute_js(f"""
                                        document.getElementById('inspect-output').innerHTML = '<span class="error">❌ Unsupported file type: ' + {suffix_escaped} + '</span>';
                                    """)
                                except Exception:
                                    pass  # UI update failed, continue
                        except Exception as inspect_error:
                            DEBUG_LOGGER.error(f"[INSPECT] Error in inspection method: {inspect_error}", exc_info=True)
                            # Show error dialog
                            try:
                                self._show_inspect_error_dialog(inspect_error, f"Inspecting {file_path_obj.name}")
                            except Exception:
                                pass  # Dialog failed, continue
                            # Try to show error in UI
                            try:
                                error_msg = str(inspect_error)
                                error_msg_escaped = json.dumps(error_msg)
                                self._execute_js(f"""
                                    document.getElementById('inspect-output').innerHTML = '<span class="error">❌ Error inspecting file: ' + {error_msg_escaped} + '</span>';
                                """)
                            except Exception:
                                pass  # UI update failed, continue
                        
                        DEBUG_LOGGER.info(f"[INSPECT] ========== perform_inspection COMPLETED SUCCESSFULLY ==========")
                    except Exception as e:
                        # Catch-all for inspection logic
                        DEBUG_LOGGER.error(f"[INSPECT] ========== CRITICAL ERROR in perform_inspection ==========")
                        DEBUG_LOGGER.error(f"[INSPECT] Error type: {type(e).__name__}")
                        DEBUG_LOGGER.error(f"[INSPECT] Error message: {str(e)}")
                        DEBUG_LOGGER.error(f"[INSPECT] Full traceback:", exc_info=True)
                        import traceback
                        DEBUG_LOGGER.error(f"[INSPECT] Traceback string:\n{traceback.format_exc()}")
                        
                        # Show error dialog - wrapped in try-except
                        try:
                            self._show_inspect_error_dialog(e, "Inspection")
                        except Exception as dialog_error:
                            DEBUG_LOGGER.error(f"[INSPECT] Failed to show error dialog: {dialog_error}", exc_info=True)
                        
                        # Also try to show error in UI - wrapped in try-except
                        try:
                            error_msg = str(e)
                            error_msg_escaped = json.dumps(error_msg)
                            DEBUG_LOGGER.debug(f"[INSPECT] Attempting to display error in UI...")
                            self._execute_js(f"""
                                document.getElementById('inspect-output').innerHTML = '<span class="error">❌ Error inspecting file: ' + {error_msg_escaped} + '</span>';
                            """)
                            DEBUG_LOGGER.debug(f"[INSPECT] Error message displayed in UI")
                        except Exception as ui_error:
                            DEBUG_LOGGER.error(f"[INSPECT] CRITICAL: Failed to display error in UI: {ui_error}", exc_info=True)
                
                # Schedule inspection on main thread using QTimer
                try:
                    DEBUG_LOGGER.info(f"[INSPECT] Scheduling inspection on main thread...")
                    QTimer.singleShot(0, perform_inspection)
                    DEBUG_LOGGER.info(f"[INSPECT] Inspection scheduled successfully")
                except Exception as timer_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to schedule inspection: {timer_error}", exc_info=True)
                    self._show_inspect_error_dialog(timer_error, "Scheduling inspection")
                    return
                
                DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_file COMPLETED (main thread) ==========")
                
            except Exception as e:
                # Catch-all for file dialog and setup errors
                DEBUG_LOGGER.error(f"[INSPECT] ========== CRITICAL ERROR in _inspect_file (main thread) ==========")
                DEBUG_LOGGER.error(f"[INSPECT] Error type: {type(e).__name__}")
                DEBUG_LOGGER.error(f"[INSPECT] Error message: {str(e)}")
                DEBUG_LOGGER.error(f"[INSPECT] Full traceback:", exc_info=True)
                import traceback
                DEBUG_LOGGER.error(f"[INSPECT] Traceback string:\n{traceback.format_exc()}")
                # Show error dialog - wrapped in try-except
                try:
                    self._show_inspect_error_dialog(e, "File selection/inspection startup")
                except Exception as dialog_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to show error dialog: {dialog_error}", exc_info=True)
        except Exception as outer_error:
            # ABSOLUTE CATCH-ALL: This should never happen, but if it does, we catch it
            DEBUG_LOGGER.error(f"[INSPECT] ========== OUTER-LEVEL CRITICAL ERROR (SHOULD NEVER HAPPEN) ==========")
            DEBUG_LOGGER.error(f"[INSPECT] Error: {outer_error}", exc_info=True)
            try:
                # Try to show error dialog
                try:
                    self._show_inspect_error_dialog(outer_error, "Outer-level error")
                except Exception:
                    # Even error dialog failed, try basic message box
                    try:
                        QMessageBox.critical(self, "Critical Error", f"Inspect feature encountered an error:\n\n{str(outer_error)}\n\nPlease check the logs for details.")
                    except Exception:
                        pass  # Even message box failed, just log it
            except Exception:
                pass  # Everything failed, just log it
    
    def _inspect_4dllm_file(self, file_path: Path):
        """Inspect 4DLLM file - SELF-HEALING: Never crashes"""
        global runner_module
        # OUTER TRY: Catch absolutely everything
        try:
            import threading
            thread_id = threading.current_thread().ident
            thread_name = threading.current_thread().name
            DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_4dllm_file STARTED (thread: {thread_id}, name: {thread_name}) ==========")
            DEBUG_LOGGER.debug(f"[INSPECT] File path: {file_path}")
            
            # Safe file existence check
            try:
                file_exists = file_path.exists()
                DEBUG_LOGGER.debug(f"[INSPECT] File exists: {file_exists}")
            except Exception:
                file_exists = False
                DEBUG_LOGGER.warning(f"[INSPECT] Could not check file existence")
            
            # INNER TRY: Actual inspection logic
            try:
                lines = []
                lines.append(f"<div style='color: #06b6d4; font-weight: 600; margin-bottom: 12px;'>=== 4DLLM File Inspection ===</div>")
                file_name_escaped = html.escape(file_path.name)
                lines.append(f"<div style='margin-bottom: 8px;'><strong>File:</strong> {file_name_escaped}</div>")
                
                DEBUG_LOGGER.debug(f"[INSPECT] Getting file size...")
                file_size_mb = file_path.stat().st_size / (1024*1024)
                DEBUG_LOGGER.debug(f"[INSPECT] File size: {file_size_mb:.2f} MB")
                lines.append(f"<div style='margin-bottom: 8px;'><strong>Size:</strong> {file_size_mb:.2f} MB</div>")
                
                # Get FOURDLLM_MAGIC from runner_module (safe access)
                DEBUG_LOGGER.debug(f"[INSPECT] Getting FOURDLLM_MAGIC from runner_module...")
                try:
                    runner_module_available = (runner_module is not None)
                except NameError:
                    runner_module_available = False
                except Exception as rm_check_error:
                    DEBUG_LOGGER.warning(f"[INSPECT] Error checking runner_module: {rm_check_error}")
                    runner_module_available = False
                
                if not runner_module_available:
                    runner_module = None
                
                DEBUG_LOGGER.debug(f"[INSPECT] runner_module_available: {runner_module_available}")
                FOURDLLM_MAGIC = b'4DLL'  # Default value
                if runner_module_available and runner_module:
                    DEBUG_LOGGER.debug(f"[INSPECT] runner_module has FOURDLLM_MAGIC: {hasattr(runner_module, 'FOURDLLM_MAGIC')}")
                    if hasattr(runner_module, 'FOURDLLM_MAGIC'):
                        FOURDLLM_MAGIC = runner_module.FOURDLLM_MAGIC
                        DEBUG_LOGGER.debug(f"[INSPECT] FOURDLLM_MAGIC value: {FOURDLLM_MAGIC}")
                DEBUG_LOGGER.debug(f"[INSPECT] Using FOURDLLM_MAGIC: {FOURDLLM_MAGIC}")
                
                # Read file header
                DEBUG_LOGGER.debug(f"[INSPECT] Opening file for reading...")
                try:
                    with open(file_path, "rb") as f:
                        DEBUG_LOGGER.debug(f"[INSPECT] File opened successfully, reading magic bytes...")
                        magic = f.read(4)
                        DEBUG_LOGGER.debug(f"[INSPECT] Read {len(magic)} magic bytes: {magic}")
                        if len(magic) < 4:
                            DEBUG_LOGGER.error(f"[INSPECT] File too short: only {len(magic)} bytes")
                            lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ ERROR: File too short (less than 4 bytes)</div>")
                            html_content = "".join(lines)
                            self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                            return
                        
                        DEBUG_LOGGER.debug(f"[INSPECT] Comparing magic: {magic} vs {FOURDLLM_MAGIC}")
                        if magic != FOURDLLM_MAGIC:
                            magic_hex = magic.hex() if magic else "empty"
                            DEBUG_LOGGER.error(f"[INSPECT] Invalid magic bytes: {magic_hex} (expected: {FOURDLLM_MAGIC.hex()})")
                            lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ ERROR: Not a valid 4DLLM file (bad magic: {magic_hex})</div>")
                            html_content = "".join(lines)
                            self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                            return
                        
                        DEBUG_LOGGER.debug(f"[INSPECT] Magic bytes match! Reading version...")
                        version_bytes = f.read(4)
                        DEBUG_LOGGER.debug(f"[INSPECT] Read {len(version_bytes)} version bytes")
                        if len(version_bytes) < 4:
                            DEBUG_LOGGER.error(f"[INSPECT] Cannot read version: only {len(version_bytes)} bytes")
                            lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ ERROR: File too short (cannot read version)</div>")
                            html_content = "".join(lines)
                            self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                            return
                        
                        version = struct.unpack("<I", version_bytes)[0]
                        DEBUG_LOGGER.debug(f"[INSPECT] File version: {version}")
                        lines.append(f"<div style='margin-bottom: 8px;'><strong>Version:</strong> {version}</div>")
                except Exception as e:
                    DEBUG_LOGGER.error(f"[INSPECT] Exception reading file header: {type(e).__name__}: {e}", exc_info=True)
                    lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ ERROR: Failed to read file header: {html.escape(str(e))}</div>")
                    html_content = "".join(lines)
                    self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                    return
                
                # Use runner module to read TOC and sections
                DEBUG_LOGGER.debug(f"[INSPECT] Checking runner_module availability...")
                DEBUG_LOGGER.debug(f"[INSPECT] runner_module_available: {runner_module_available}")
                if runner_module_available:
                    DEBUG_LOGGER.debug(f"[INSPECT] runner_module has read_and_validate_toc: {hasattr(runner_module, 'read_and_validate_toc')}")
                
                if runner_module_available and hasattr(runner_module, 'read_and_validate_toc'):
                    DEBUG_LOGGER.debug(f"[INSPECT] Calling read_and_validate_toc...")
                    try:
                        entries, toc_offset = runner_module.read_and_validate_toc(file_path)
                        DEBUG_LOGGER.debug(f"[INSPECT] TOC read successfully: {len(entries)} entries, offset: {toc_offset}")
                        lines.append(f"<div style='margin-bottom: 8px;'><strong>TOC Offset:</strong> {toc_offset}</div>")
                        lines.append(f"<div style='margin-bottom: 8px;'><strong>Section Count:</strong> {len(entries)}</div>")
                        
                        # Get section constants safely
                        SECTION_GGUF_DATA = getattr(runner_module, 'SECTION_GGUF_DATA', 0x01)
                        SECTION_METADATA = getattr(runner_module, 'SECTION_METADATA', 0x03)
                        SECTION_PYTHON_SCRIPT = getattr(runner_module, 'SECTION_PYTHON_SCRIPT', 0x02)
                        SECTION_SCRIPT_CONFIG = getattr(runner_module, 'SECTION_SCRIPT_CONFIG', 0x04)
                        
                        # Analyze sections
                        gguf_count = sum(1 for e in entries if e.section_type == SECTION_GGUF_DATA)
                        metadata_count = sum(1 for e in entries if e.section_type == SECTION_METADATA)
                        script_count = sum(1 for e in entries if e.section_type == SECTION_PYTHON_SCRIPT)
                        
                        lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Section Breakdown:</strong></div>")
                        lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• GGUF Data: {gguf_count}</div>")
                        lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• Metadata: {metadata_count}</div>")
                        lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• Python Scripts: {script_count}</div>")
                        
                        # Read metadata if available
                        metadata_entry = next((e for e in entries if e.section_type == SECTION_METADATA), None)
                        if metadata_entry and hasattr(runner_module, 'read_section_small'):
                            try:
                                section = runner_module.read_section_small(file_path, metadata_entry)
                                if section and hasattr(section, 'data'):
                                    try:
                                        metadata = json.loads(section.data.decode('utf-8'))
                                        metadata_json = json.dumps(metadata, indent=2)
                                        # Escape HTML entities in the JSON string for safe display
                                        metadata_escaped = html.escape(metadata_json)
                                        lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Metadata:</strong></div>")
                                        lines.append(f"<div style='margin-left: 20px; background: rgba(15, 23, 42, 0.6); padding: 8px; border-radius: 6px; font-family: monospace; font-size: 11px; white-space: pre-wrap; max-height: 300px; overflow-y: auto;'>")
                                        lines.append(metadata_escaped)
                                        lines.append("</div>")
                                    except (json.JSONDecodeError, UnicodeDecodeError, AttributeError) as e:
                                        error_msg = html.escape(str(e))
                                        lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Could not parse metadata: {error_msg}</div>")
                            except Exception as e:
                                error_msg = html.escape(str(e))
                                lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Could not read metadata: {error_msg}</div>")
                        
                        # Read script config if available
                        script_config_entry = next((e for e in entries if e.section_type == SECTION_SCRIPT_CONFIG), None)
                        if script_config_entry and hasattr(runner_module, 'read_section_small'):
                            try:
                                section = runner_module.read_section_small(file_path, script_config_entry)
                                if section and hasattr(section, 'data'):
                                    try:
                                        script_config = json.loads(section.data.decode('utf-8'))
                                        script_config_json = json.dumps(script_config, indent=2)
                                        # Escape HTML entities in the JSON string for safe display
                                        script_config_escaped = html.escape(script_config_json)
                                        lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Script Config:</strong></div>")
                                        lines.append(f"<div style='margin-left: 20px; background: rgba(15, 23, 42, 0.6); padding: 8px; border-radius: 6px; font-family: monospace; font-size: 11px; white-space: pre-wrap;'>")
                                        lines.append(script_config_escaped)
                                        lines.append("</div>")
                                    except (json.JSONDecodeError, UnicodeDecodeError, AttributeError) as e:
                                        error_msg = html.escape(str(e))
                                        lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Could not parse script config: {error_msg}</div>")
                            except Exception as e:
                                error_msg = html.escape(str(e))
                                lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Could not read script config: {error_msg}</div>")
                    except Exception as e:
                        lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ Error reading TOC: {e}</div>")
                        DEBUG_LOGGER.error(f"Error reading 4DLLM TOC: {e}", exc_info=True)
                else:
                    # Try to read TOC manually even without runner_module
                    lines.append(f"<div style='color: #fbbf24; margin-top: 12px;'>⚠️ Runner module not available - using basic inspection</div>")
                    try:
                        # Try to find and read TOC manually
                        file_size = file_path.stat().st_size
                        scan_back = min(4 * 1024 * 1024, file_size)  # Scan last 4MB or file size, whichever is smaller
                        start = max(0, file_size - scan_back)
                        
                        with open(file_path, "rb") as f:
                            f.seek(start)
                            data = f.read()
                            toc_pos = data.find(b"TOC1")
                            
                            if toc_pos >= 0:
                                toc_offset = start + toc_pos
                                lines.append(f"<div style='margin-top: 12px; margin-bottom: 8px;'><strong>TOC Offset:</strong> {toc_offset} (0x{toc_offset:x})</div>")
                                
                                # Read TOC header manually
                                f.seek(toc_offset)
                                toc_magic = f.read(4)
                                if toc_magic == b"TOC1":
                                    toc_version_bytes = f.read(4)
                                    toc_version = struct.unpack("<I", toc_version_bytes)[0]
                                    count_bytes = f.read(4)
                                    section_count = struct.unpack("<I", count_bytes)[0]
                                    
                                    lines.append(f"<div style='margin-bottom: 8px;'><strong>TOC Version:</strong> {toc_version}</div>")
                                    lines.append(f"<div style='margin-bottom: 8px;'><strong>Section Count:</strong> {section_count}</div>")
                                    
                                    # Try to read section entries (basic info only)
                                    if section_count > 0 and section_count < 1000:  # Sanity check
                                        SECTION_ENTRY_SIZE = 40  # sizeof(TocEntry) = 40 bytes
                                        try:
                                            entries_data = f.read(SECTION_ENTRY_SIZE * section_count)
                                            if len(entries_data) == SECTION_ENTRY_SIZE * section_count:
                                                # Section type constants (from 4dllm_runner.py)
                                                SECTION_GGUF_DATA = 0x01
                                                SECTION_PYTHON_SCRIPT = 0x02
                                                SECTION_METADATA = 0x03
                                                SECTION_SCRIPT_CONFIG = 0x04
                                                
                                                gguf_count = 0
                                                script_count = 0
                                                metadata_count = 0
                                                config_count = 0
                                                
                                                for i in range(section_count):
                                                    offset = i * SECTION_ENTRY_SIZE
                                                    section_type = struct.unpack("<B", entries_data[offset:offset+1])[0]
                                                    if section_type == SECTION_GGUF_DATA:
                                                        gguf_count += 1
                                                    elif section_type == SECTION_PYTHON_SCRIPT:
                                                        script_count += 1
                                                    elif section_type == SECTION_METADATA:
                                                        metadata_count += 1
                                                    elif section_type == SECTION_SCRIPT_CONFIG:
                                                        config_count += 1
                                                
                                                lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Section Breakdown:</strong></div>")
                                                lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• GGUF Data: {gguf_count}</div>")
                                                lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• Python Scripts: {script_count}</div>")
                                                lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• Metadata: {metadata_count}</div>")
                                                if config_count > 0:
                                                    lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'>• Script Config: {config_count}</div>")
                                        except Exception as entry_error:
                                            DEBUG_LOGGER.warning(f"[INSPECT] Could not read section entries: {entry_error}")
                                    else:
                                        lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Invalid section count: {section_count}</div>")
                            else:
                                lines.append(f"<div style='color: #fbbf24; margin-top: 12px;'>⚠️ TOC not found in file</div>")
                    except Exception as toc_error:
                        DEBUG_LOGGER.warning(f"[INSPECT] Could not read TOC manually: {toc_error}", exc_info=True)
                        lines.append(f"<div style='color: #fbbf24; margin-top: 8px;'>⚠️ Could not read TOC: {html.escape(str(toc_error))}</div>")
                
                # Safe HTML content generation and UI update
                try:
                    html_content = "".join(lines)
                    DEBUG_LOGGER.debug(f"[INSPECT] Generated HTML content length: {len(html_content)} characters")
                    DEBUG_LOGGER.debug(f"[INSPECT] Calling _execute_js to update UI...")
                    self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                    DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_4dllm_file COMPLETED SUCCESSFULLY ==========")
                except Exception as ui_update_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to update UI: {ui_update_error}", exc_info=True)
                    # Try to show at least a basic error message
                    try:
                        self._execute_js(f"document.getElementById('inspect-output').innerHTML = '<span class=\"error\">❌ Error updating UI display</span>';")
                    except Exception:
                        pass  # Even basic UI update failed
                
            except Exception as e:
                # Catch-all for inspection logic errors
                DEBUG_LOGGER.error(f"[INSPECT] ========== CRITICAL ERROR in _inspect_4dllm_file ==========")
                DEBUG_LOGGER.error(f"[INSPECT] Error type: {type(e).__name__}")
                DEBUG_LOGGER.error(f"[INSPECT] Error message: {str(e)}")
                DEBUG_LOGGER.error(f"[INSPECT] Full traceback:", exc_info=True)
                import traceback
                DEBUG_LOGGER.error(f"[INSPECT] Traceback string:\n{traceback.format_exc()}")
                
                # Show error dialog (thread-safe via QTimer) - wrapped in try-except
                try:
                    try:
                        file_name = file_path.name if file_path else "unknown"
                    except Exception:
                        file_name = "unknown"
                    self._show_inspect_error_dialog(e, f"4DLLM file inspection: {file_name}")
                except Exception as dialog_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to show error dialog: {dialog_error}", exc_info=True)
                
                # Also try to show error in UI - wrapped in try-except
                try:
                    error_msg = str(e)
                    error_msg_escaped = json.dumps(error_msg)
                    DEBUG_LOGGER.debug(f"[INSPECT] Attempting to display error in UI...")
                    self._execute_js(f"""
                        document.getElementById('inspect-output').innerHTML = '<span class="error">❌ Error inspecting 4DLLM file: ' + {error_msg_escaped} + '</span>';
                    """)
                    DEBUG_LOGGER.debug(f"[INSPECT] Error message displayed in UI")
                except Exception as ui_error:
                    DEBUG_LOGGER.error(f"[INSPECT] CRITICAL: Failed to display error in UI: {ui_error}", exc_info=True)
        except Exception as outer_error:
            # ABSOLUTE CATCH-ALL: Should never happen, but catch it anyway
            DEBUG_LOGGER.error(f"[INSPECT] ========== OUTER-LEVEL CRITICAL ERROR in _inspect_4dllm_file ==========")
            DEBUG_LOGGER.error(f"[INSPECT] Error: {outer_error}", exc_info=True)
            try:
                self._show_inspect_error_dialog(outer_error, "4DLLM file inspection (outer-level error)")
            except Exception:
                pass  # Even error dialog failed, just log it
    
    def _inspect_gguf_file(self, file_path: Path):
        """Inspect GGUF file - SELF-HEALING: Never crashes"""
        # OUTER TRY: Catch absolutely everything
        try:
            import threading
            thread_id = threading.current_thread().ident
            thread_name = threading.current_thread().name
            DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_gguf_file STARTED (thread: {thread_id}, name: {thread_name}) ==========")
            DEBUG_LOGGER.debug(f"[INSPECT] File path: {file_path}")
            
            # Safe file existence check
            try:
                file_exists = file_path.exists()
                DEBUG_LOGGER.debug(f"[INSPECT] File exists: {file_exists}")
            except Exception:
                file_exists = False
                DEBUG_LOGGER.warning(f"[INSPECT] Could not check file existence")
            
            # INNER TRY: Actual inspection logic
            try:
                lines = []
                lines.append(f"<div style='color: #06b6d4; font-weight: 600; margin-bottom: 12px;'>=== GGUF File Inspection ===</div>")
                file_name_escaped = html.escape(file_path.name)
                lines.append(f"<div style='margin-bottom: 8px;'><strong>File:</strong> {file_name_escaped}</div>")
                
                DEBUG_LOGGER.debug(f"[INSPECT] Getting file size...")
                file_size_mb = file_path.stat().st_size / (1024*1024)
                DEBUG_LOGGER.debug(f"[INSPECT] File size: {file_size_mb:.2f} MB")
                lines.append(f"<div style='margin-bottom: 8px;'><strong>Size:</strong> {file_size_mb:.2f} MB</div>")
                
                # Use editor module to read GGUF metadata
                DEBUG_LOGGER.debug(f"[INSPECT] Checking editor_module availability...")
                DEBUG_LOGGER.debug(f"[INSPECT] editor_module is None: {editor_module is None}")
                if editor_module:
                    DEBUG_LOGGER.debug(f"[INSPECT] Creating GGUFEditor instance...")
                    try:
                        editor = editor_module.GGUFEditor(str(file_path))
                        DEBUG_LOGGER.debug(f"[INSPECT] GGUFEditor created successfully")
                        DEBUG_LOGGER.debug(f"[INSPECT] Has load_metadata_only: {hasattr(editor, 'load_metadata_only')}")
                        
                        DEBUG_LOGGER.debug(f"[INSPECT] Loading metadata...")
                        metadata = editor.load_metadata_only() if hasattr(editor, 'load_metadata_only') else editor.load()
                        DEBUG_LOGGER.debug(f"[INSPECT] Metadata loaded: {len(metadata)} keys")
                        
                        lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Metadata Keys:</strong> {len(metadata)}</div>")
                        
                        # Show key metadata fields
                        important_keys = [
                            'general.architecture', 'general.name', 'general.quantization_version',
                            'llama.context_length', 'llama.embedding_length', 'llama.block_count',
                            'llama.attention.head_count', 'llama.rope.dimension_count',
                            'llama.system_prompt', 'chat_template.system_prompt',
                            'llama.persona_prompt', 'chat_template.persona_prompt'
                        ]
                        
                        lines.append(f"<div style='margin-top: 16px; margin-bottom: 8px;'><strong>Key Information:</strong></div>")
                        for key in important_keys:
                            if key in metadata:
                                value = metadata[key]
                                if isinstance(value, (dict, list)):
                                    value_str = json.dumps(value, indent=2)
                                else:
                                    value_str = str(value)
                                # Truncate long values
                                if len(value_str) > 200:
                                    value_str = value_str[:197] + "..."
                                lines.append(f"<div style='margin-left: 20px; margin-bottom: 4px;'><strong>{key}:</strong> {value_str}</div>")
                        
                        # Show metadata preview (avoid massive HTML payloads)
                        MAX_METADATA_KEYS = 500
                        MAX_METADATA_CHARS = 200000
                        total_keys = len(metadata)
                        preview_items = list(metadata.items())[:MAX_METADATA_KEYS]
                        preview_metadata = dict(preview_items)
                        preview_json = json.dumps(preview_metadata, indent=2)
                        truncated = False
                        if len(preview_json) > MAX_METADATA_CHARS:
                            preview_json = preview_json[:MAX_METADATA_CHARS] + "\n... [truncated]"
                            truncated = True

                        lines.append(
                            "<div style='margin-top: 16px; margin-bottom: 8px;'><strong>All Metadata (preview):</strong></div>"
                        )
                        lines.append(
                            f"<div style='margin-left: 20px; margin-bottom: 8px; color: #94a3b8; font-size: 11px;'>"
                            f"Showing {min(total_keys, MAX_METADATA_KEYS)} of {total_keys} keys"
                            f"{' (output truncated)' if truncated else ''}."
                            f"</div>"
                        )
                        lines.append(
                            "<div style='margin-left: 20px; background: rgba(15, 23, 42, 0.6); padding: 8px; border-radius: 6px; "
                            "font-family: monospace; font-size: 11px; white-space: pre-wrap; max-height: 400px; overflow-y: auto;'>"
                        )
                        lines.append(preview_json)
                        lines.append("</div>")
                        
                    except Exception as e:
                        DEBUG_LOGGER.error(f"[INSPECT] Exception reading GGUF metadata: {type(e).__name__}: {e}", exc_info=True)
                        lines.append(f"<div style='color: #f87171; margin-top: 12px;'>❌ Error reading GGUF metadata: {e}</div>")
                else:
                    DEBUG_LOGGER.warning(f"[INSPECT] Editor module not available")
                    lines.append(f"<div style='color: #fbbf24; margin-top: 12px;'>⚠️ Editor module not available - cannot read GGUF metadata</div>")
                
                html_content = "".join(lines)
                DEBUG_LOGGER.debug(f"[INSPECT] Generated HTML content length: {len(html_content)} characters")
                DEBUG_LOGGER.debug(f"[INSPECT] Calling _execute_js to update UI...")
                self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(html_content)};")
                DEBUG_LOGGER.info(f"[INSPECT] ========== _inspect_gguf_file COMPLETED SUCCESSFULLY ==========")
            except Exception as e:
                DEBUG_LOGGER.error(f"[INSPECT] Error in inner inspection logic: {e}", exc_info=True)
                error_msg = html.escape(str(e))
                error_html = f"<div style='color: #f87171; margin-top: 12px;'>❌ Error during inspection: {error_msg}</div>"
                try:
                    self._execute_js(f"document.getElementById('inspect-output').innerHTML = {json.dumps(error_html)};")
                except Exception as ui_error:
                    DEBUG_LOGGER.error(f"[INSPECT] Failed to update UI with error: {ui_error}")
        except Exception as e:
            DEBUG_LOGGER.error(f"[INSPECT] ========== CRITICAL ERROR in _inspect_gguf_file ==========")
            DEBUG_LOGGER.error(f"[INSPECT] Error type: {type(e).__name__}")
            DEBUG_LOGGER.error(f"[INSPECT] Error message: {str(e)}")
            DEBUG_LOGGER.error(f"[INSPECT] Full traceback:", exc_info=True)
            import traceback
            DEBUG_LOGGER.error(f"[INSPECT] Traceback string:\n{traceback.format_exc()}")
            
            # Show error dialog (thread-safe via QTimer)
            try:
                self._show_inspect_error_dialog(e, f"GGUF file inspection: {file_path.name}")
            except Exception as dialog_error:
                DEBUG_LOGGER.error(f"[INSPECT] Failed to show error dialog: {dialog_error}", exc_info=True)
            
            # Also try to show error in UI
            try:
                error_msg = str(e)
                error_msg_escaped = json.dumps(error_msg)
                DEBUG_LOGGER.debug(f"[INSPECT] Attempting to display error in UI...")
                self._execute_js_safe(f"""
                    document.getElementById('inspect-output').innerHTML = '<span class="error">❌ Error inspecting GGUF file: ' + {error_msg_escaped} + '</span>';
                """)
                DEBUG_LOGGER.debug(f"[INSPECT] Error message displayed in UI")
            except Exception as ui_error:
                DEBUG_LOGGER.error(f"[INSPECT] CRITICAL: Failed to display error in UI: {ui_error}", exc_info=True)
    
    def _refresh_inspect(self):
        """Refresh inspection of current file"""
        if hasattr(self, '_inspect_file_path') and self._inspect_file_path:
            self._inspect_file()
        else:
            QMessageBox.warning(self, "No File", "Please load a file first.")
    
    def _select_profile(self, profile: str):
        if profile in self.profiles:
            self.current_profile = self.profiles[profile].copy()
    
    def _toggle_unsafe(self, enabled: bool):
        self.unsafe_enabled = enabled
        color = "#f87171" if enabled else "#34d399"
        text = "UNSAFE" if enabled else "SAFE"
        self._execute_js(f"""
            const badge = document.getElementById('safety-badge');
            badge.textContent = '{text}';
            badge.style.background = 'rgba(' + (parseInt('{color}'.slice(1, 3), 16)) + ', ' + (parseInt('{color}'.slice(3, 5), 16)) + ', ' + (parseInt('{color}'.slice(5, 7), 16)) + ', 0.2)';
            badge.style.border = '1px solid {color}';
        """)
    
    def _send_chat_message(self, text: str):
        """Send chat message ONLY when user explicitly sends it"""
        if not self.proc:
            self._append_terminal("[No process running]\n", "warning")
            return
        
        if not self.proc.stdin:
            self._append_terminal("[Process stdin not available]\n", "warning")
            return
        
        # Don't send if AI is still generating
        if self.waiting_for_ai:
            self._append_terminal("[Please wait for AI to finish responding]\n", "warning")
            return
        
        try:
            # Show user message FIRST
            self._append_terminal(f"You> {text}\n", "command")
            
            # Write message with newline and flush IMMEDIATELY
            message = text + "\n"
            self.proc.stdin.write(message)
            self.proc.stdin.flush()
            
            # Set waiting state - AI will respond
            import time
            DEBUG_LOGGER.info(f"[SEND] Sending message: '{text[:100]}', setting waiting_for_ai=True")
            self.waiting_for_ai = True
            self.last_output_time = time.time()
            self.last_ai_line_time = 0  # Reset - will be set when we see "AI>" line
            # Clear log cache when starting new cycle
            self._last_log_times.clear()
            DEBUG_LOGGER.debug(f"[SEND] State initialized: waiting_for_ai={self.waiting_for_ai}, last_output_time={self.last_output_time:.3f}")
            self._start_streaming_indicator()
            self._start_ai_timeout()
            self._reset_inactivity_timeout()
            
            DEBUG_LOGGER.debug(f"Sent chat message: {text}")
        except BrokenPipeError:
            self._append_terminal("[Process ended]\n", "error")
            self.proc = None
            self.waiting_for_ai = False
        except Exception as e:
            error_msg = f"Error sending message: {e}"
            DEBUG_LOGGER.error(error_msg, exc_info=True)
            self._append_terminal(f"[Error: {error_msg}]\n", "error")
            self.waiting_for_ai = False
    
    def _build_command(self) -> List[str]:
        """Build command to run - supports both 4DLLM and GGUF files"""
        if not self.run_path:
            self._show_error_dialog("No File Selected", "Please select a .4dllm or .gguf file to run.", "ERR-BUILD-CMD-NO-FILE-001")
            return []
        
        if self.is_gguf_file:
            # For GGUF files, use llama-cpp-python directly via a simple wrapper
            # We'll create a simple inline Python script that uses llama-cpp-python
            import tempfile
            import textwrap
            
            # Create a temporary Python script that runs the GGUF file
            script_content = textwrap.dedent(f"""
                import sys
                try:
                    from llama_cpp import Llama
                except ImportError:
                    print("ERROR: llama-cpp-python not installed. Please install it with: pip install llama-cpp-python", file=sys.stderr)
                    sys.exit(1)
                
                import os
                os.environ['PYTHONUNBUFFERED'] = '1'
                
                model_path = {repr(str(self.run_path))}
                n_ctx = {self.current_profile.get("n_ctx", 4096)}
                n_threads = {self.current_profile.get("threads", 4)}
                n_gpu_layers = {self.current_profile.get("gpu_layers", 0)}
                max_tokens = {self.current_profile.get("max_tokens", 256)}
                temperature = {self.current_profile.get("temp", 0.7)}
                
                print(f"[GGUF] Loading model: {{model_path}}", flush=True)
                try:
                    llm = Llama(
                        model_path=model_path,
                        n_ctx=n_ctx,
                        n_threads=n_threads,
                        n_gpu_layers=n_gpu_layers,
                        verbose=False
                    )
                    print(f"[GGUF] Model loaded successfully", flush=True)
                    print("\\n[GGUF] Chat ready. Type /exit to quit.\\n", flush=True)
                    
                    history = []
                    # Print initial "You> " prompt
                    print("You> ", end="", flush=True)
                    
                    while True:
                        try:
                            # Read user input (GUI already displays "You> " prefix when user sends message)
                            user_msg = sys.stdin.readline().strip()
                            if not user_msg:
                                continue
                            if user_msg.lower() == "/exit":
                                break
                            
                            # Format chat prompt
                            prompt_parts = []
                            for u, a in history:
                                prompt_parts.append(f"User: {{u}}\\nAssistant: {{a}}")
                            prompt_parts.append(f"User: {{user_msg}}\\nAssistant:")
                            prompt = "\\n".join(prompt_parts)
                            
                            # Generate response - print "AI> " prefix FIRST
                            print("AI> ", end="", flush=True)
                            try:
                                response = llm(
                                    prompt,
                                    max_tokens=max_tokens,
                                    temperature=temperature,
                                    stop=["User:", "\\nUser:", "\\n\\nUser:", "Assistant:"],
                                    echo=False
                                )
                                
                                # Extract text from response - llama-cpp-python returns Completion object or dict
                                text = ""
                                try:
                                    # Try dict-style access first (for dict responses)
                                    if response and isinstance(response, dict) and 'choices' in response:
                                        if len(response['choices']) > 0:
                                            choice = response['choices'][0]
                                            if isinstance(choice, dict) and 'text' in choice:
                                                text = str(choice['text']).strip()
                                    # Try attribute access (for Completion objects)
                                    elif response and hasattr(response, 'choices'):
                                        choices = response.choices
                                        if choices and len(choices) > 0:
                                            choice = choices[0]
                                            if hasattr(choice, 'text'):
                                                text = str(choice.text).strip()
                                            elif isinstance(choice, dict) and 'text' in choice:
                                                text = str(choice['text']).strip()
                                    # Try direct dict access with get (fallback)
                                    elif response:
                                        choices = response.get('choices', []) if isinstance(response, dict) else []
                                        if choices and len(choices) > 0:
                                            choice = choices[0]
                                            text = str(choice.get('text', '') if isinstance(choice, dict) else getattr(choice, 'text', '')).strip()
                                except Exception as extract_err:
                                    print(f"(Error extracting response: {{extract_err}})", file=sys.stderr, flush=True)
                                
                                # Print response text (will be on same line as "AI> ")
                                if text:
                                    print(text, flush=True)
                                else:
                                    print("(No response generated)", flush=True)
                                
                                # Add to history if we got a valid response
                                if text:
                                    history.append((user_msg, text))
                            except Exception as gen_err:
                                print(f"ERROR generating response: {{gen_err}}", file=sys.stderr, flush=True)
                                import traceback
                                traceback.print_exc(file=sys.stderr)
                                # Continue to next iteration but don't print "You> " yet
                                continue
                            
                            # Print "You> " prompt for next input (this signals AI finished to GUI)
                            print("You> ", end="", flush=True)
                                
                        except EOFError:
                            break
                        except KeyboardInterrupt:
                            break
                        except Exception as e:
                            print(f"ERROR: {{e}}", file=sys.stderr, flush=True)
                            import traceback
                            traceback.print_exc(file=sys.stderr)
                except Exception as e:
                    print(f"ERROR loading model: {{e}}", file=sys.stderr, flush=True)
                    import traceback
                    traceback.print_exc(file=sys.stderr)
                    sys.exit(1)
            """)
            
            # Write script to temp file
            temp_script = tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False)
            temp_script.write(script_content)
            temp_script.close()
            temp_script_path = Path(temp_script.name)
            
            cmd = [sys.executable, "-u", str(temp_script_path)]
            # Store temp script path for cleanup later
            self._gguf_temp_script = temp_script_path
            return cmd
        else:
            # For 4DLLM files, use the existing runner script
            runner_script = Path(__file__).parent / "4dllm_runner.py"
            cmd = [
                sys.executable,
                str(runner_script),
                "--file", str(self.run_path),
                "--backend", self.current_profile.get("backend", "llama_cpp"),
                "--n-ctx", str(self.current_profile.get("n_ctx", "4096")),
                "--threads", str(self.current_profile.get("threads", "4")),
                "--gpu-layers", str(self.current_profile.get("gpu_layers", "0")),
                "--max-tokens", str(self.current_profile.get("max_tokens", "256")),
                "--temp", str(self.current_profile.get("temp", "0.7")),
            ]
            if self.unsafe_enabled:
                cmd.append("--unsafe-modules")
            return cmd
    
    def _update_file_selection(self):
        if self.run_path:
            file_name_escaped = self.run_path.name.replace("'", "\\'").replace("\\", "\\\\")
            file_type = 'GGUF' if self.is_gguf_file else '4DLLM'
            file_type_escaped = file_type.replace("'", "\\'")
            update_js = f"""
                const el = document.getElementById('selected-file');
                el.innerHTML = '{file_name_escaped}<br><span style="font-size: 11px; color: #64748b;">({file_type_escaped} file)</span>';
            """
            self._execute_js(update_js)
    
    def _on_process_finished(self):
        """Called when process finishes"""
        self._stop_streaming_indicator()
        self._cancel_ai_timeout()
        self._cancel_inactivity_timeout()
        self.waiting_for_ai = False
        self.last_ai_line_time = 0
        self._append_terminal("\n[Process finished]\n", "info")
        # Clean up
        if self.proc:
            try:
                if self.proc.stdin:
                    self.proc.stdin.close()
            except:
                pass
        self.proc = None
        self.reader_thread = None
        
        # Clean up temporary GGUF script if it exists
        if hasattr(self, '_gguf_temp_script') and self._gguf_temp_script:
            try:
                if self._gguf_temp_script.exists():
                    self._gguf_temp_script.unlink()
                    DEBUG_LOGGER.info(f"Cleaned up temporary GGUF script: {self._gguf_temp_script}")
            except Exception as e:
                DEBUG_LOGGER.warning(f"Failed to clean up temporary script: {e}")
            self._gguf_temp_script = None
    
    def _on_page_loaded(self, success: bool):
        """Called when HTML page finishes loading"""
        if success:
            # Give WebChannel time to initialize
            QTimer.singleShot(200, self._verify_bridge)
    
    def _verify_bridge(self):
        """Verify WebChannel bridge is connected"""
        self._execute_js("""
            if (window.bridge && typeof window.bridge.sendChatMessage === 'function') {
                console.log('✓ Bridge connected successfully');
            } else {
                console.warn('⚠ Bridge not fully connected, retrying...');
                setTimeout(function() {
                    if (typeof qt !== 'undefined' && qt.webChannelTransport) {
                        new QWebChannel(qt.webChannelTransport, function(channel) {
                            window.bridge = channel.objects.bridge;
                            console.log('✓ Bridge connected on retry');
                        });
                    }
                }, 500);
            }
        """)


def exception_handler(exc_type, exc_value, exc_traceback):
    """Global exception handler to catch unhandled exceptions"""
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    
    import traceback
    error_msg = f"Unhandled exception: {exc_type.__name__}: {exc_value}"
    traceback_str = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
    
    DEBUG_LOGGER.error("="*80)
    DEBUG_LOGGER.error("UNHANDLED EXCEPTION CAUGHT BY GLOBAL HANDLER")
    DEBUG_LOGGER.error(error_msg)
    DEBUG_LOGGER.error(traceback_str)
    DEBUG_LOGGER.error("="*80)
    
    # Try to show error popup if QApplication exists
    try:
        app = QApplication.instance()
        if app:
            msg_box = QMessageBox()
            msg_box.setIcon(QMessageBox.Icon.Critical)
            msg_box.setWindowTitle("Critical Error")
            msg_box.setText(f"<b>Unhandled Exception</b><br><br>{error_msg}")
            msg_box.setDetailedText(traceback_str)
            msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
            msg_box.exec()
    except:
        pass
    
    # Also print to stderr
    print(f"CRITICAL ERROR: {error_msg}", file=sys.stderr)
    print(traceback_str, file=sys.stderr)


def main():
    """Main entry point"""
    # Set global exception handler
    sys.excepthook = exception_handler
    
    if not HAS_PYQT6:
        print("ERROR: PyQt6 or PySide6 is required for this interface.")
        print("Install with: pip install PyQt6 PyQt6-WebEngine")
        return 1
    
    app = QApplication(sys.argv)
    
    # Set dark theme
    app.setStyle('Fusion')
    palette = QPalette()
    palette.setColor(QPalette.ColorRole.Window, QColor(15, 23, 42))
    palette.setColor(QPalette.ColorRole.WindowText, QColor(226, 232, 240))
    app.setPalette(palette)
    
    window = WorldClassStudio()
    window.show()
    
    return app.exec()


if __name__ == "__main__":
    raise SystemExit(main())

