const { invoke } = window.__TAURI__.core;

let messageInput;
let messagesContainer;
let chatForm;
let sendBtn;
let statusText;
let settingsPanel;
let chatView;
let characterSelect;
let characterHeaderName;
let newCharacterBtn;

let currentCharacter = null;
let pendingAvatarPath = null;

// Theme definitions
const themes = {
  dark: {
    name: 'Dark (Default)',
    bgPrimary: '#1a1a1a',
    bgSecondary: '#252525',
    bgTertiary: '#2f2f2f',
    textPrimary: '#e8e8e8',
    textSecondary: '#a0a0a0',
    accent: '#6366f1',
    accentHover: '#4f46e5',
    userMsg: '#4f46e5',
    assistantMsg: '#2f2f2f',
    border: '#3a3a3a',
    gradient: 'linear-gradient(135deg, #1a1a1a 0%, #2a1a2a 100%)',
    glow: 'rgba(99, 102, 241, 0.1)'
  },
  darker: {
    name: 'Darker',
    bgPrimary: '#0a0a0a',
    bgSecondary: '#141414',
    bgTertiary: '#1a1a1a',
    textPrimary: '#e0e0e0',
    textSecondary: '#909090',
    accent: '#7c3aed',
    accentHover: '#6d28d9',
    userMsg: '#6d28d9',
    assistantMsg: '#1a1a1a',
    border: '#2a2a2a',
    gradient: 'linear-gradient(135deg, #0a0a0a 0%, #1a0a1a 100%)',
    glow: 'rgba(124, 58, 237, 0.1)'
  },
  midnight: {
    name: 'Midnight Blue',
    bgPrimary: '#0f1419',
    bgSecondary: '#1a2332',
    bgTertiary: '#243447',
    textPrimary: '#e6f1ff',
    textSecondary: '#8892a0',
    accent: '#3b82f6',
    accentHover: '#2563eb',
    userMsg: '#1e40af',
    assistantMsg: '#243447',
    border: '#2d3e54',
    gradient: 'linear-gradient(135deg, #0f1419 0%, #1a2845 100%)',
    glow: 'rgba(59, 130, 246, 0.1)'
  },
  forest: {
    name: 'Forest',
    bgPrimary: '#0d1b14',
    bgSecondary: '#162820',
    bgTertiary: '#1f352b',
    textPrimary: '#e8f5e9',
    textSecondary: '#90a89f',
    accent: '#10b981',
    accentHover: '#059669',
    userMsg: '#047857',
    assistantMsg: '#1f352b',
    border: '#2d4a3a',
    gradient: 'linear-gradient(135deg, #0d1b14 0%, #1a2820 100%)',
    glow: 'rgba(16, 185, 129, 0.1)'
  },
  sunset: {
    name: 'Sunset',
    bgPrimary: '#1a1214',
    bgSecondary: '#261a1e',
    bgTertiary: '#332228',
    textPrimary: '#fde8e8',
    textSecondary: '#b89090',
    accent: '#f97316',
    accentHover: '#ea580c',
    userMsg: '#c2410c',
    assistantMsg: '#332228',
    border: '#4a3238',
    gradient: 'linear-gradient(135deg, #1a1214 0%, #2a1a1e 100%)',
    glow: 'rgba(249, 115, 22, 0.1)'
  },
  light: {
    name: 'Light',
    bgPrimary: '#ffffff',
    bgSecondary: '#f5f5f5',
    bgTertiary: '#e8e8e8',
    textPrimary: '#1a1a1a',
    textSecondary: '#666666',
    accent: '#6366f1',
    accentHover: '#4f46e5',
    userMsg: '#6366f1',
    assistantMsg: '#f0f0f0',
    border: '#d0d0d0',
    gradient: 'linear-gradient(135deg, #ffffff 0%, #f5f0ff 100%)',
    glow: 'rgba(99, 102, 241, 0.05)'
  }
};

// Apply theme
function applyTheme(themeName) {
  const theme = themes[themeName];
  if (!theme) return;

  const root = document.documentElement;
  root.style.setProperty('--bg-primary', theme.bgPrimary);
  root.style.setProperty('--bg-secondary', theme.bgSecondary);
  root.style.setProperty('--bg-tertiary', theme.bgTertiary);
  root.style.setProperty('--text-primary', theme.textPrimary);
  root.style.setProperty('--text-secondary', theme.textSecondary);
  root.style.setProperty('--accent', theme.accent);
  root.style.setProperty('--accent-hover', theme.accentHover);
  root.style.setProperty('--user-msg', theme.userMsg);
  root.style.setProperty('--assistant-msg', theme.assistantMsg);
  root.style.setProperty('--border', theme.border);

  // Update gradient and glow
  const appContainer = document.querySelector('.app-container');
  if (appContainer) {
    appContainer.style.background = theme.gradient;
    const glow = appContainer.querySelector('::before');
  }

  // Store preference
  localStorage.setItem('claudia-theme', themeName);
}

// Load saved theme
function loadSavedTheme() {
  const savedTheme = localStorage.getItem('claudia-theme') || 'dark';
  const themeSelect = document.getElementById('theme-select');
  if (themeSelect) {
    themeSelect.value = savedTheme;
  }
  applyTheme(savedTheme);
}

// Apply view mode
function applyViewMode(mode) {
  const body = document.body;

  // Remove all view mode classes
  body.classList.remove('view-compact', 'view-cozy', 'view-comfortable');

  // Add the selected mode
  body.classList.add(`view-${mode}`);

  // Store preference
  localStorage.setItem('claudia-view-mode', mode);
}

// Load saved view mode
function loadSavedViewMode() {
  const savedMode = localStorage.getItem('claudia-view-mode') || 'cozy';
  const viewModeSelect = document.getElementById('view-mode-select');
  if (viewModeSelect) {
    viewModeSelect.value = savedMode;
  }
  applyViewMode(savedMode);
}

// Apply font size
function applyFontSize(scale) {
  const root = document.documentElement;

  // Calculate font size based on scale (80-140%)
  const baseFontSize = 14; // Default base size in px
  const newFontSize = (baseFontSize * scale) / 100;

  root.style.setProperty('--base-font-size', `${newFontSize}px`);
  root.style.fontSize = `${newFontSize}px`;

  // Update the display value
  const fontSizeValue = document.getElementById('font-size-value');
  if (fontSizeValue) {
    fontSizeValue.textContent = `${scale}%`;
  }

  // Store preference
  localStorage.setItem('claudia-font-size', scale.toString());
}

// Load saved font size
function loadSavedFontSize() {
  const savedSize = parseInt(localStorage.getItem('claudia-font-size') || '100');
  const fontSizeSlider = document.getElementById('font-size-slider');
  if (fontSizeSlider) {
    fontSizeSlider.value = savedSize;
  }
  applyFontSize(savedSize);
}

// Export chat history
async function exportChatHistory() {
  try {
    setStatus('Exporting chat...', 'default');
    const filePath = await invoke('export_chat_history');
    setStatus('Chat exported successfully!', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
    console.log('Chat exported to:', filePath);
  } catch (error) {
    console.error('Export failed:', error);
    if (error && !error.toString().includes('cancelled')) {
      setStatus(`Export failed: ${error}`, 'error');
      setTimeout(() => setStatus('Ready'), 3000);
    } else {
      setStatus('Ready');
    }
  }
}

// Import chat history
async function importChatHistory() {
  try {
    setStatus('Importing chat...', 'default');
    const messageCount = await invoke('import_chat_history');

    // Reload the chat history
    await loadChatHistory();

    setStatus(`Imported ${messageCount} messages successfully!`, 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Import failed:', error);
    if (error === 'No file selected' || error.toString().includes('cancelled')) {
      setStatus('Ready');
    } else {
      setStatus(`Import failed: ${error}`, 'error');
      setTimeout(() => setStatus('Ready'), 3000);
    }
  }
}

// Helper function to get avatar URL
async function getAvatarUrl(avatarFilename) {
  if (!avatarFilename) return null;
  try {
    const fullPath = await invoke('get_avatar_full_path', { avatarFilename });
    console.log('Avatar full path:', fullPath);

    // Try to use convertFileSrc if available
    if (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.convertFileSrc) {
      const url = window.__TAURI__.core.convertFileSrc(fullPath);
      console.log('Converted URL:', url);
      return url;
    } else {
      // Fallback to using the path directly with proper protocol
      const url = `asset://localhost/${fullPath}`;
      console.log('Using asset protocol URL:', url);
      return url;
    }
  } catch (error) {
    console.error('Failed to get avatar URL for', avatarFilename, ':', error);
    return null;
  }
}

// Show avatar in modal
function showAvatarModal(avatarUrl) {
  const modal = document.getElementById('avatar-modal');
  const modalImg = document.getElementById('avatar-modal-img');

  modalImg.src = avatarUrl;
  modal.style.display = 'flex';

  // Fade in animation
  modal.style.opacity = '0';
  setTimeout(() => {
    modal.style.opacity = '1';
    modal.style.transition = 'opacity 0.2s ease';
  }, 10);
}

// Hide avatar modal
function hideAvatarModal() {
  const modal = document.getElementById('avatar-modal');
  modal.style.opacity = '0';
  setTimeout(() => {
    modal.style.display = 'none';
  }, 200);
}

// Make avatar clickable
function makeAvatarClickable(avatarElement, avatarUrl) {
  if (!avatarUrl) return;

  avatarElement.addEventListener('click', (e) => {
    e.stopPropagation();
    showAvatarModal(avatarUrl);
  });
}

// Format timestamp for display
function formatTimestamp(timestamp) {
  if (!timestamp) return '';

  const date = new Date(timestamp);
  const now = new Date();
  const diff = now - date;
  const seconds = Math.floor(diff / 1000);
  const minutes = Math.floor(seconds / 60);
  const hours = Math.floor(minutes / 60);
  const days = Math.floor(hours / 24);

  // Just now (less than 1 minute)
  if (seconds < 60) {
    return 'Just now';
  }

  // Minutes ago (less than 1 hour)
  if (minutes < 60) {
    return `${minutes}m ago`;
  }

  // Today (show time)
  if (days === 0) {
    return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
  }

  // Yesterday
  if (days === 1) {
    return `Yesterday at ${date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })}`;
  }

  // This week (show day name)
  if (days < 7) {
    return date.toLocaleDateString('en-US', { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true });
  }

  // Older (show date)
  return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
}

// Auto-resize textarea
function autoResize(textarea) {
  textarea.style.height = 'auto';
  textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
}

// Helper function to render assistant message content with character name
function renderAssistantContent(contentDiv, messageText) {
  // Clear existing content
  contentDiv.innerHTML = '';

  // Add character name indicator
  if (currentCharacter && currentCharacter.name) {
    const nameIndicator = document.createElement('div');
    nameIndicator.className = 'character-name-indicator';
    nameIndicator.textContent = currentCharacter.name;
    contentDiv.appendChild(nameIndicator);
  }

  // Add message content
  const messageContent = document.createElement('div');
  messageContent.innerHTML = marked.parse(messageText);
  contentDiv.appendChild(messageContent);

  // Apply syntax highlighting to code blocks
  messageContent.querySelectorAll('pre code').forEach((block) => {
    hljs.highlightElement(block);
    addCopyButtonToCode(block);
  });

  return messageContent;
}

// Add message to chat
function addMessage(content, isUser = false, skipActions = false, timestamp = null) {
  const messageDiv = document.createElement('div');
  messageDiv.className = `message ${isUser ? 'user' : 'assistant'}`;

  const avatar = document.createElement('div');
  avatar.className = 'avatar-circle';

  // Set avatar image for assistant messages
  if (!isUser && currentCharacter && currentCharacter.avatar_path) {
    getAvatarUrl(currentCharacter.avatar_path).then(url => {
      if (url) {
        avatar.style.backgroundImage = `url('${url}')`;
        makeAvatarClickable(avatar, url);
      }
    });
  }

  const contentDiv = document.createElement('div');
  contentDiv.className = 'message-content';

  if (isUser) {
    // User messages: plain text
    const p = document.createElement('p');
    p.textContent = content;
    contentDiv.appendChild(p);

    // Add timestamp if provided
    if (timestamp) {
      const timestampDiv = document.createElement('div');
      timestampDiv.className = 'message-timestamp';
      timestampDiv.textContent = formatTimestamp(timestamp);
      contentDiv.appendChild(timestampDiv);
    }
  } else {
    // Assistant messages: render as markdown
    // Add character name indicator if character exists
    if (currentCharacter && currentCharacter.name) {
      const nameIndicator = document.createElement('div');
      nameIndicator.className = 'character-name-indicator';
      nameIndicator.textContent = currentCharacter.name;
      contentDiv.appendChild(nameIndicator);
    }

    const messageContent = document.createElement('div');
    messageContent.innerHTML = marked.parse(content);
    contentDiv.appendChild(messageContent);

    // Apply syntax highlighting to code blocks
    messageContent.querySelectorAll('pre code').forEach((block) => {
      hljs.highlightElement(block);

      // Add copy button to code blocks
      const pre = block.parentElement;
      if (!pre.querySelector('.copy-btn')) {
        const copyBtn = document.createElement('button');
        copyBtn.className = 'copy-btn';
        copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
          <path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
        </svg>`;
        copyBtn.title = 'Copy code';
        copyBtn.addEventListener('click', () => {
          navigator.clipboard.writeText(block.textContent);
          copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M3 8l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>`;
          copyBtn.classList.add('copied');
          setTimeout(() => {
            copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
              <rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
              <path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
            </svg>`;
            copyBtn.classList.remove('copied');
          }, 2000);
        });
        pre.style.position = 'relative';
        pre.appendChild(copyBtn);
      }
    });

    // Add timestamp if provided
    if (timestamp) {
      const timestampDiv = document.createElement('div');
      timestampDiv.className = 'message-timestamp';
      timestampDiv.textContent = formatTimestamp(timestamp);
      contentDiv.appendChild(timestampDiv);
    }
  }

  // Build message structure
  if (!skipActions) {
    const actionsDiv = document.createElement('div');
    actionsDiv.className = 'message-actions';

    if (isUser) {
      // User message: simple structure with edit button
      const editBtn = document.createElement('button');
      editBtn.className = 'message-action-btn';
      editBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M10 1L13 4L5 12H2V9L10 1Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      editBtn.title = 'Edit message';
      editBtn.addEventListener('click', () => handleEditMessage(messageDiv, content));
      actionsDiv.appendChild(editBtn);

      // Copy message button
      const copyBtn = document.createElement('button');
      copyBtn.className = 'message-action-btn';
      copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" fill="none"/>
        <path d="M5 3V2C5 1.44772 5.44772 1 6 1H12C12.5523 1 13 1.44772 13 2V8C13 8.55228 12.5523 9 12 9H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>`;
      copyBtn.title = 'Copy message';
      copyBtn.addEventListener('click', () => {
        navigator.clipboard.writeText(content);
        // Visual feedback
        const originalHTML = copyBtn.innerHTML;
        copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
          <path d="M2 7l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>`;
        setTimeout(() => {
          copyBtn.innerHTML = originalHTML;
        }, 2000);
      });
      actionsDiv.appendChild(copyBtn);

      // Pin button
      const pinBtn = document.createElement('button');
      pinBtn.className = 'message-action-btn message-pin-btn';
      pinBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M7 11V5M4.5 5L7 2.5L9.5 5M7 11L7 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      pinBtn.title = 'Pin message';
      pinBtn.addEventListener('click', () => handleTogglePin(messageDiv));
      actionsDiv.appendChild(pinBtn);

      // Hide button
      const hideBtn = document.createElement('button');
      hideBtn.className = 'message-action-btn message-hide-btn';
      hideBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M1 7C1 7 3 3 7 3C11 3 13 7 13 7C13 7 11 11 7 11C3 11 1 7 1 7Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        <circle cx="7" cy="7" r="2" stroke="currentColor" stroke-width="1.5"/>
      </svg>`;
      hideBtn.title = 'Hide message';
      hideBtn.addEventListener('click', () => handleToggleHidden(messageDiv));
      actionsDiv.appendChild(hideBtn);

      // Delete button
      const deleteBtn = document.createElement('button');
      deleteBtn.className = 'message-action-btn message-delete-btn';
      deleteBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M2 4H12M5 4V3C5 2.44772 5.44772 2 6 2H8C8.55228 2 9 2.44772 9 3V4M11 4L10.5 11C10.5 11.5523 10.0523 12 9.5 12H4.5C3.94772 12 3.5 11.5523 3.5 11L3 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      deleteBtn.title = 'Delete message';
      deleteBtn.addEventListener('click', () => handleDeleteMessage(messageDiv));
      actionsDiv.appendChild(deleteBtn);

      messageDiv.appendChild(contentDiv);
      messageDiv.appendChild(actionsDiv);
    } else {
      // Assistant message: structure with swipe controls
      const regenerateBtn = document.createElement('button');
      regenerateBtn.className = 'message-action-btn';
      regenerateBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M1 7C1 3.68629 3.68629 1 7 1C10.3137 1 13 3.68629 13 7C13 10.3137 10.3137 13 7 13C5.5 13 4.16667 12.3333 3 11.3333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
        <path d="M1 10V7H4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      regenerateBtn.title = 'Regenerate response';
      regenerateBtn.addEventListener('click', () => handleRegenerateMessage(messageDiv));
      actionsDiv.appendChild(regenerateBtn);

      // Continue button
      const continueBtn = document.createElement('button');
      continueBtn.className = 'message-action-btn message-continue-btn';
      continueBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M3 7H11M11 7L7 3M11 7L7 11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      continueBtn.title = 'Continue message';
      continueBtn.addEventListener('click', () => handleContinueMessage(messageDiv));
      actionsDiv.appendChild(continueBtn);

      // Pin button
      const pinBtn = document.createElement('button');
      pinBtn.className = 'message-action-btn message-pin-btn';
      pinBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M7 11V5M4.5 5L7 2.5L9.5 5M7 11L7 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      pinBtn.title = 'Pin message';
      pinBtn.addEventListener('click', () => handleTogglePin(messageDiv));
      actionsDiv.appendChild(pinBtn);

      // Hide button
      const hideBtn = document.createElement('button');
      hideBtn.className = 'message-action-btn message-hide-btn';
      hideBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M1 7C1 7 3 3 7 3C11 3 13 7 13 7C13 7 11 11 7 11C3 11 1 7 1 7Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        <circle cx="7" cy="7" r="2" stroke="currentColor" stroke-width="1.5"/>
      </svg>`;
      hideBtn.title = 'Hide message';
      hideBtn.addEventListener('click', () => handleToggleHidden(messageDiv));
      actionsDiv.appendChild(hideBtn);

      // Copy message button
      const copyMsgBtn = document.createElement('button');
      copyMsgBtn.className = 'message-action-btn';
      copyMsgBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" fill="none"/>
        <path d="M5 3V2C5 1.44772 5.44772 1 6 1H12C12.5523 1 13 1.44772 13 2V8C13 8.55228 12.5523 9 12 9H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>`;
      copyMsgBtn.title = 'Copy message';
      copyMsgBtn.addEventListener('click', () => {
        navigator.clipboard.writeText(content);
        // Visual feedback
        const originalHTML = copyMsgBtn.innerHTML;
        copyMsgBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
          <path d="M2 7l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>`;
        setTimeout(() => {
          copyMsgBtn.innerHTML = originalHTML;
        }, 2000);
      });
      actionsDiv.appendChild(copyMsgBtn);

      // Delete button
      const deleteBtn = document.createElement('button');
      deleteBtn.className = 'message-action-btn message-delete-btn';
      deleteBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M2 4H12M5 4V3C5 2.44772 5.44772 2 6 2H8C8.55228 2 9 2.44772 9 3V4M11 4L10.5 11C10.5 11.5523 10.0523 12 9.5 12H4.5C3.94772 12 3.5 11.5523 3.5 11L3 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      deleteBtn.title = 'Delete message';
      deleteBtn.addEventListener('click', () => handleDeleteMessage(messageDiv));
      actionsDiv.appendChild(deleteBtn);

      // Create swipe wrapper
      const swipeWrapper = document.createElement('div');
      swipeWrapper.style.display = 'flex';
      swipeWrapper.style.flexDirection = 'column';
      swipeWrapper.appendChild(contentDiv);

      const swipeControls = createSwipeControls(messageDiv);
      swipeWrapper.appendChild(swipeControls);

      messageDiv.appendChild(swipeWrapper);
      messageDiv.appendChild(actionsDiv);
    }
  } else {
    messageDiv.appendChild(contentDiv);
  }

  if (!isUser) {
    messageDiv.insertBefore(avatar, messageDiv.firstChild);
  }
  messagesContainer.appendChild(messageDiv);
  messagesContainer.scrollTop = messagesContainer.scrollHeight;

  return messageDiv;
}

// Create swipe controls for assistant messages
function createSwipeControls(messageDiv) {
  const swipeControls = document.createElement('div');
  swipeControls.className = 'swipe-controls';

  const prevBtn = document.createElement('button');
  prevBtn.className = 'swipe-btn swipe-prev';
  prevBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
    <path d="M7.5 2L3.5 6L7.5 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
  </svg>`;
  prevBtn.title = 'Previous response';
  prevBtn.addEventListener('click', () => handleSwipeNavigation(messageDiv, -1));

  const counter = document.createElement('span');
  counter.className = 'swipe-counter';
  counter.textContent = '1/1';

  const nextBtn = document.createElement('button');
  nextBtn.className = 'swipe-btn swipe-next';
  nextBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
    <path d="M4.5 2L8.5 6L4.5 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
  </svg>`;
  nextBtn.title = 'Next response';
  nextBtn.addEventListener('click', () => handleSwipeNavigation(messageDiv, 1));

  swipeControls.appendChild(prevBtn);
  swipeControls.appendChild(counter);
  swipeControls.appendChild(nextBtn);

  // Initially hide if only one swipe
  updateSwipeControls(messageDiv, 0, 1);

  return swipeControls;
}

// Update swipe controls state
function updateSwipeControls(messageDiv, current, total) {
  const swipeControls = messageDiv.querySelector('.swipe-controls');
  if (!swipeControls) return;

  const counter = swipeControls.querySelector('.swipe-counter');
  const prevBtn = swipeControls.querySelector('.swipe-prev');
  const nextBtn = swipeControls.querySelector('.swipe-next');

  counter.textContent = `${current + 1}/${total}`;
  prevBtn.disabled = current === 0;
  nextBtn.disabled = current === total - 1;

  // Show controls if more than one swipe
  if (total > 1) {
    swipeControls.classList.add('always-visible');
  } else {
    swipeControls.classList.remove('always-visible');
  }
}

// Handle swipe navigation
async function handleSwipeNavigation(messageDiv, direction) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  console.log('handleSwipeNavigation called:', { messageIndex, direction });

  try {
    const swipeInfo = await invoke('navigate_swipe', { messageIndex, direction });
    console.log('Received swipeInfo:', swipeInfo);

    // Update message content
    const contentDiv = messageDiv.querySelector('.message-content');
    console.log('Found contentDiv:', contentDiv);
    console.log('Setting content to:', swipeInfo.content);
    renderAssistantContent(contentDiv, swipeInfo.content);

    // Update swipe controls
    updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
  } catch (error) {
    console.error('Failed to navigate swipe:', error);
  }
}

// Handle editing a user message
async function handleEditMessage(messageDiv, originalContent) {
  const contentDiv = messageDiv.querySelector('.message-content');
  const actionsDiv = messageDiv.querySelector('.message-actions');

  // Hide action buttons during edit
  actionsDiv.style.display = 'none';

  // Create edit form
  const editForm = document.createElement('form');
  editForm.className = 'message-edit-form';

  const textarea = document.createElement('textarea');
  textarea.className = 'message-edit-textarea';
  textarea.value = originalContent;
  textarea.rows = 3;
  autoResize(textarea);

  const editActions = document.createElement('div');
  editActions.className = 'message-edit-actions';

  const saveBtn = document.createElement('button');
  saveBtn.type = 'submit';
  saveBtn.className = 'message-edit-btn';
  saveBtn.textContent = 'Save & Resend';

  const cancelBtn = document.createElement('button');
  cancelBtn.type = 'button';
  cancelBtn.className = 'message-edit-btn';
  cancelBtn.textContent = 'Cancel';

  editActions.appendChild(saveBtn);
  editActions.appendChild(cancelBtn);
  editForm.appendChild(textarea);
  editForm.appendChild(editActions);

  // Auto-resize on input
  textarea.addEventListener('input', () => autoResize(textarea));

  // Replace content with edit form
  const originalHTML = contentDiv.innerHTML;
  contentDiv.innerHTML = '';
  contentDiv.appendChild(editForm);

  textarea.focus();
  textarea.setSelectionRange(textarea.value.length, textarea.value.length);

  // Handle cancel
  cancelBtn.addEventListener('click', () => {
    contentDiv.innerHTML = originalHTML;
    actionsDiv.style.display = 'flex';
  });

  // Handle save
  editForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    const newContent = textarea.value.trim();

    if (!newContent || newContent === originalContent) {
      contentDiv.innerHTML = originalHTML;
      actionsDiv.style.display = 'flex';
      return;
    }

    // Get the index of this message
    const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
    const messageIndex = allMessages.indexOf(messageDiv);

    // Disable form
    saveBtn.disabled = true;
    cancelBtn.disabled = true;
    saveBtn.textContent = 'Saving...';

    try {
      // Truncate history from this point
      await invoke('truncate_history_from', { index: messageIndex });

      // Remove all messages from this point forward in UI
      while (messagesContainer.children[messageIndex]) {
        messagesContainer.children[messageIndex].remove();
      }

      // Send the edited message
      await sendMessage(newContent);
    } catch (error) {
      console.error('Failed to edit message:', error);
      contentDiv.innerHTML = originalHTML;
      actionsDiv.style.display = 'flex';
      addMessage(`Error editing message: ${error}`, false);
    }
  });
}

// Handle regenerating an assistant message
async function handleRegenerateMessage(messageDiv) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  if (messageIndex === -1) {
    console.error('Message not found in list');
    return;
  }

  const regenerateBtn = messageDiv.querySelector('.message-action-btn');
  regenerateBtn.disabled = true;
  regenerateBtn.classList.add('loading');

  try {
    setStatus('Regenerating response...', 'default');

    // Use the new regenerate_at_index command which works on any message
    const swipeInfo = await invoke('regenerate_at_index', { messageIndex });

    // Update the message content
    const contentDiv = messageDiv.querySelector('.message-content');
    renderAssistantContent(contentDiv, swipeInfo.content);

    // Update swipe controls
    updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);

    setStatus('Regeneration complete', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to regenerate message:', error);
    setStatus(`Regeneration failed: ${error}`, 'error');
  } finally {
    regenerateBtn.disabled = false;
    regenerateBtn.classList.remove('loading');
  }
}

// Generate a new swipe for an existing assistant message
async function generateSwipe(messageDiv, userMessage) {
  setStatus('Regenerating response...', 'default');

  // Check if streaming is enabled
  let streamEnabled = false;
  try {
    const config = await invoke('get_api_config');
    streamEnabled = config.stream || false;
  } catch (error) {
    console.error('Failed to get config:', error);
  }

  if (streamEnabled) {
    await generateSwipeStream(messageDiv, userMessage);
  } else {
    await generateSwipeNonStream(messageDiv, userMessage);
  }
}

// Generate swipe using non-streaming
async function generateSwipeNonStream(messageDiv, userMessage) {
  try {
    const response = await invoke('generate_response_only');

    // Add as a swipe
    const swipeInfo = await invoke('add_swipe_to_last_assistant', { content: response });

    // Update the message content
    const contentDiv = messageDiv.querySelector('.message-content');
    renderAssistantContent(contentDiv, swipeInfo.content);

    // Update swipe controls
    updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);

    setStatus('Regeneration complete', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
    const regenerateBtn = messageDiv.querySelector('.message-action-btn');
    if (regenerateBtn) {
      regenerateBtn.disabled = false;
      regenerateBtn.classList.remove('loading');
    }
  } catch (error) {
    setStatus(`Regeneration failed: ${error.substring(0, 40)}...`, 'error');
    const regenerateBtn = messageDiv.querySelector('.message-action-btn');
    if (regenerateBtn) {
      regenerateBtn.disabled = false;
      regenerateBtn.classList.remove('loading');
    }
    addMessage(`Error regenerating message: ${error}`, false);
  }
}

// Generate swipe using streaming
async function generateSwipeStream(messageDiv, userMessage) {
  setStatus('Streaming regeneration...', 'streaming');

  let fullContent = '';
  const contentDiv = messageDiv.querySelector('.message-content');

  // Set up event listeners for streaming
  const { listen } = window.__TAURI__.event;

  const tokenUnlisten = await listen('chat-token', (event) => {
    const token = event.payload;
    fullContent += token;

    // Update content with markdown rendering
    renderAssistantContent(contentDiv, fullContent);

    messagesContainer.scrollTop = messagesContainer.scrollHeight;
  });

  const completeUnlisten = await listen('chat-complete', async () => {
    // Add as a swipe
    try {
      const swipeInfo = await invoke('add_swipe_to_last_assistant', { content: fullContent });

      // Update swipe controls
      updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
    } catch (error) {
      console.error('Failed to add swipe:', error);
    }

    setStatus('Regeneration complete', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
    const regenerateBtn = messageDiv.querySelector('.message-action-btn');
    if (regenerateBtn) {
      regenerateBtn.disabled = false;
      regenerateBtn.classList.remove('loading');
    }
    tokenUnlisten();
    completeUnlisten();
  });

  try {
    await invoke('generate_response_stream');
  } catch (error) {
    tokenUnlisten();
    completeUnlisten();
    setStatus(`Regeneration failed: ${error.substring(0, 40)}...`, 'error');
    const regenerateBtn = messageDiv.querySelector('.message-action-btn');
    if (regenerateBtn) {
      regenerateBtn.disabled = false;
      regenerateBtn.classList.remove('loading');
    }
    addMessage(`Error: ${error}`, false);
  }
}

// Helper to add copy button to code blocks
function addCopyButtonToCode(block) {
  const pre = block.parentElement;
  if (pre && !pre.querySelector('.copy-btn')) {
    const copyBtn = document.createElement('button');
    copyBtn.className = 'copy-btn';
    copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
      <path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
    </svg>`;
    copyBtn.title = 'Copy code';
    copyBtn.addEventListener('click', () => {
      navigator.clipboard.writeText(block.textContent);
      copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
        <path d="M3 8l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      copyBtn.classList.add('copied');
      setTimeout(() => {
        copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
          <path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
        </svg>`;
        copyBtn.classList.remove('copied');
      }, 2000);
    });
    pre.style.position = 'relative';
    pre.appendChild(copyBtn);
  }
}

// Handle deleting a message
async function handleDeleteMessage(messageDiv) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  if (messageIndex === -1) {
    console.error('Message not found in list');
    return;
  }

  // Confirm deletion
  if (!confirm('Are you sure you want to delete this message? This cannot be undone.')) {
    return;
  }

  try {
    await invoke('delete_message_at_index', { messageIndex });
    messageDiv.remove();
    await updateTokenCount();
    setStatus('Message deleted', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to delete message:', error);
    setStatus(`Delete failed: ${error}`, 'error');
  }
}

// Handle toggling message pin status
async function handleTogglePin(messageDiv) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  if (messageIndex === -1) {
    console.error('Message not found in list');
    return;
  }

  try {
    const isPinned = await invoke('toggle_message_pin', { messageIndex });

    // Update visual indicator
    const pinBtn = messageDiv.querySelector('.message-pin-btn');
    if (isPinned) {
      messageDiv.classList.add('pinned');
      pinBtn.classList.add('active');
      pinBtn.title = 'Unpin message';
    } else {
      messageDiv.classList.remove('pinned');
      pinBtn.classList.remove('active');
      pinBtn.title = 'Pin message';
    }
  } catch (error) {
    console.error('Failed to toggle pin:', error);
    setStatus(`Pin toggle failed: ${error}`, 'error');
  }
}

// Handle toggling message hidden status
async function handleToggleHidden(messageDiv) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  if (messageIndex === -1) {
    console.error('Message not found in list');
    return;
  }

  try {
    const isHidden = await invoke('toggle_message_hidden', { messageIndex });

    // Update visual indicator
    const hideBtn = messageDiv.querySelector('.message-hide-btn');
    if (isHidden) {
      messageDiv.classList.add('hidden-message');
      hideBtn.classList.add('active');
      hideBtn.title = 'Unhide message';
      // Update icon to "eye-off"
      hideBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M10 5L11.5 3.5M3.5 10.5L5 9M1 13L13 1M5.5 6C5.19 6.31 5 6.74 5 7.22C5 8.2 5.8 9 6.78 9C7.26 9 7.69 8.81 8 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>`;
    } else {
      messageDiv.classList.remove('hidden-message');
      hideBtn.classList.remove('active');
      hideBtn.title = 'Hide message';
      // Update icon back to "eye"
      hideBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M1 7C1 7 3 3 7 3C11 3 13 7 13 7C13 7 11 11 7 11C3 11 1 7 1 7Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        <circle cx="7" cy="7" r="2" stroke="currentColor" stroke-width="1.5"/>
      </svg>`;
    }

    await updateTokenCount();
  } catch (error) {
    console.error('Failed to toggle hidden:', error);
    setStatus(`Hide toggle failed: ${error}`, 'error');
  }
}

// Handle continuing an incomplete message
async function handleContinueMessage(messageDiv) {
  const allMessages = Array.from(messagesContainer.querySelectorAll('.message'));
  const messageIndex = allMessages.indexOf(messageDiv);

  if (messageIndex === -1) {
    console.error('Message not found in list');
    return;
  }

  const continueBtn = messageDiv.querySelector('.message-continue-btn');
  continueBtn.disabled = true;
  continueBtn.classList.add('loading');

  try {
    setStatus('Continuing message...', 'default');
    const continuedText = await invoke('continue_message', { messageIndex });

    // The backend appends to the message, so we just need to reload the content
    const contentDiv = messageDiv.querySelector('.message-content');
    const swipeInfo = await invoke('get_swipe_info', { messageIndex });
    renderAssistantContent(contentDiv, swipeInfo.content);

    setStatus('Message continued', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
    await updateTokenCount();
  } catch (error) {
    console.error('Failed to continue message:', error);
    setStatus(`Continue failed: ${error}`, 'error');
  } finally {
    continueBtn.disabled = false;
    continueBtn.classList.remove('loading');
  }
}

// Extract message sending logic into separate function
async function sendMessage(message, isRegenerate = false) {
  if (!isRegenerate) {
    addMessage(message, true, false, Date.now());
  }

  sendBtn.disabled = true;
  messageInput.disabled = true;
  setStatus('Connecting to API...', 'default');

  // Check if streaming is enabled
  let streamEnabled = false;
  try {
    const config = await invoke('get_api_config');
    streamEnabled = config.stream || false;
  } catch (error) {
    console.error('Failed to get config:', error);
  }

  if (streamEnabled) {
    // Use streaming
    setStatus('Streaming response...', 'streaming');

    let streamingMessageDiv = null;
    let streamingContentDiv = null;
    let fullContent = '';

    // Create streaming message container
    const messageDiv = document.createElement('div');
    messageDiv.className = 'message assistant';

    const avatar = document.createElement('div');
    avatar.className = 'avatar-circle';

    // Set avatar image for streaming messages
    if (currentCharacter && currentCharacter.avatar_path) {
      getAvatarUrl(currentCharacter.avatar_path).then(url => {
        if (url) {
          avatar.style.backgroundImage = `url('${url}')`;
          makeAvatarClickable(avatar, url);
        }
      });
    }

    const contentDiv = document.createElement('div');
    contentDiv.className = 'message-content';

    // Create swipe wrapper for assistant messages
    const swipeWrapper = document.createElement('div');
    swipeWrapper.style.display = 'flex';
    swipeWrapper.style.flexDirection = 'column';
    swipeWrapper.appendChild(contentDiv);

    const swipeControls = createSwipeControls(messageDiv);
    swipeWrapper.appendChild(swipeControls);

    messageDiv.appendChild(avatar);
    messageDiv.appendChild(swipeWrapper);
    messagesContainer.appendChild(messageDiv);

    streamingMessageDiv = messageDiv;
    streamingContentDiv = contentDiv;

    // Set up event listeners for streaming
    const { listen } = window.__TAURI__.event;

    const tokenUnlisten = await listen('chat-token', (event) => {
      const token = event.payload;
      fullContent += token;

      // Update content with markdown rendering
      renderAssistantContent(streamingContentDiv, fullContent);

      messagesContainer.scrollTop = messagesContainer.scrollHeight;
    });

    const completeUnlisten = await listen('chat-complete', () => {
      // Add regenerate button after streaming completes
      const actionsDiv = document.createElement('div');
      actionsDiv.className = 'message-actions';

      const regenerateBtn = document.createElement('button');
      regenerateBtn.className = 'message-action-btn';
      regenerateBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path d="M1 7C1 3.68629 3.68629 1 7 1C10.3137 1 13 3.68629 13 7C13 10.3137 10.3137 13 7 13C5.5 13 4.16667 12.3333 3 11.3333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
        <path d="M1 10V7H4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>`;
      regenerateBtn.title = 'Regenerate response';
      regenerateBtn.addEventListener('click', () => handleRegenerateMessage(streamingMessageDiv));
      actionsDiv.appendChild(regenerateBtn);
      streamingMessageDiv.appendChild(actionsDiv);

      setStatus('Response complete', 'success');
      setTimeout(() => setStatus('Ready'), 2000);
      sendBtn.disabled = false;
      messageInput.disabled = false;
      messageInput.focus();
      tokenUnlisten();
      completeUnlisten();
    });

    try {
      await invoke('chat_stream', { message });
    } catch (error) {
      tokenUnlisten();
      completeUnlisten();
      if (streamingMessageDiv) {
        streamingMessageDiv.remove();
      }
      if (error.includes('not configured')) {
        addMessage('API not configured. Please configure your API settings.', false);
        setStatus('API not configured', 'error');
        setTimeout(showSettings, 1000);
      } else {
        addMessage(`Error: ${error}`, false);
        setStatus(`Error: ${error.substring(0, 50)}...`, 'error');
      }
      sendBtn.disabled = false;
      messageInput.disabled = false;
      messageInput.focus();
    }
  } else {
    // Use non-streaming
    showTypingIndicator();

    try {
      const response = await invoke('chat', { message });
      removeTypingIndicator();
      addMessage(response, false);
      setStatus('Response complete', 'success');
      setTimeout(() => setStatus('Ready'), 2000);
    } catch (error) {
      removeTypingIndicator();
      if (error.includes('not configured')) {
        addMessage('API not configured. Please configure your API settings.', false);
        setStatus('API not configured', 'error');
        setTimeout(showSettings, 1000);
      } else {
        addMessage(`Error: ${error}`, false);
        setStatus(`Error: ${error.substring(0, 50)}...`, 'error');
      }
    } finally {
      sendBtn.disabled = false;
      messageInput.disabled = false;
      messageInput.focus();
    }
  }
}

// Show typing indicator
function showTypingIndicator() {
  const typingDiv = document.createElement('div');
  typingDiv.className = 'message assistant';
  typingDiv.id = 'typing-indicator';

  const indicatorDiv = document.createElement('div');
  indicatorDiv.className = 'typing-indicator';

  for (let i = 0; i < 3; i++) {
    const dot = document.createElement('div');
    dot.className = 'typing-dot';
    indicatorDiv.appendChild(dot);
  }

  typingDiv.appendChild(indicatorDiv);
  messagesContainer.appendChild(typingDiv);
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
}

// Remove typing indicator
function removeTypingIndicator() {
  const indicator = document.getElementById('typing-indicator');
  if (indicator) {
    indicator.remove();
  }
}

// Update status with optional styling
function setStatus(text, type = 'default') {
  statusText.textContent = text;

  // Remove all status classes
  statusText.classList.remove('streaming', 'error', 'success');

  // Add appropriate class based on type
  if (type === 'streaming') {
    statusText.classList.add('streaming');
  } else if (type === 'error') {
    statusText.classList.add('error');
  } else if (type === 'success') {
    statusText.classList.add('success');
  }
}

// Show/hide settings
async function showSettings() {
  const overlay = document.getElementById('settings-overlay');
  settingsPanel.classList.add('open');
  overlay.classList.add('show');
  await loadCharacterSettings();
}

function hideSettings() {
  const overlay = document.getElementById('settings-overlay');
  settingsPanel.classList.remove('open');
  overlay.classList.remove('show');
}

// Show/hide roleplay panel
async function showRoleplayPanel() {
  const panel = document.getElementById('roleplay-panel');
  const overlay = document.getElementById('roleplay-overlay');
  panel.classList.add('open');
  overlay.classList.add('show');

  // Load roleplay settings when panel opens
  await loadRoleplaySettings();
}

function hideRoleplayPanel() {
  const panel = document.getElementById('roleplay-panel');
  const overlay = document.getElementById('roleplay-overlay');
  panel.classList.remove('open');
  overlay.classList.remove('show');
}

// Tab switching
function setupTabs() {
  // Settings tabs
  const tabBtns = document.querySelectorAll('.tab-btn');
  const tabContents = document.querySelectorAll('.tab-content');

  tabBtns.forEach(btn => {
    btn.addEventListener('click', () => {
      const targetTab = btn.getAttribute('data-tab');

      // Remove active class from all tabs and contents
      tabBtns.forEach(b => b.classList.remove('active'));
      tabContents.forEach(c => c.classList.remove('active'));

      // Add active class to clicked tab and corresponding content
      btn.classList.add('active');
      document.getElementById(`${targetTab}-tab`).classList.add('active');
    });
  });

  // Roleplay tabs
  const roleplayTabBtns = document.querySelectorAll('.roleplay-tab-btn');
  const roleplayTabContents = document.querySelectorAll('.roleplay-tab-content');

  roleplayTabBtns.forEach(btn => {
    btn.addEventListener('click', () => {
      const targetTab = btn.getAttribute('data-tab');

      // Remove active class from all roleplay tabs and contents
      roleplayTabBtns.forEach(b => b.classList.remove('active'));
      roleplayTabContents.forEach(c => c.classList.remove('active'));

      // Add active class to clicked tab and corresponding content
      btn.classList.add('active');
      document.getElementById(`${targetTab}-tab`).classList.add('active');
    });
  });
}

// Handle form submission
async function handleSubmit(e) {
  e.preventDefault();

  const message = messageInput.value.trim();
  if (!message) return;

  messageInput.value = '';
  autoResize(messageInput);

  await sendMessage(message);
}

// Settings functionality
async function handleValidate() {
  const baseUrl = document.getElementById('api-base-url').value.trim();
  const apiKey = document.getElementById('api-key').value.trim();
  const validateBtn = document.getElementById('validate-btn');
  const modelsGroup = document.getElementById('models-group');
  const modelSelect = document.getElementById('model-select');
  const saveBtn = document.getElementById('save-settings-btn');
  const validationMsg = document.getElementById('validation-message');

  if (!baseUrl || !apiKey) {
    validationMsg.textContent = 'Please fill in all fields';
    validationMsg.className = 'validation-message error';
    return;
  }

  validateBtn.disabled = true;
  validateBtn.classList.add('loading');
  validateBtn.textContent = 'Validating...';
  validationMsg.style.display = 'none';
  setStatus('Validating API...', 'default');

  try {
    const models = await invoke('validate_api', { baseUrl, apiKey });

    validationMsg.textContent = `Found ${models.length} models`;
    validationMsg.className = 'validation-message success';
    setStatus('API validated successfully', 'success');
    setTimeout(() => setStatus('Ready'), 2000);

    modelSelect.innerHTML = '<option value="">Select a model</option>';
    models.forEach(model => {
      const option = document.createElement('option');
      option.value = model;
      option.textContent = model;
      modelSelect.appendChild(option);
    });

    modelsGroup.style.display = 'flex';
    modelsGroup.classList.add('fade-in');
    saveBtn.disabled = false;
  } catch (error) {
    validationMsg.textContent = `Validation failed: ${error}`;
    validationMsg.className = 'validation-message error';
    setStatus('API validation failed', 'error');
    modelsGroup.style.display = 'none';
    saveBtn.disabled = true;
  } finally {
    validateBtn.disabled = false;
    validateBtn.classList.remove('loading');
    validateBtn.textContent = 'Validate';
  }
}

async function handleSaveSettings(e) {
  e.preventDefault();

  const baseUrl = document.getElementById('api-base-url').value.trim();
  const apiKey = document.getElementById('api-key').value.trim();
  const model = document.getElementById('model-select').value;
  const stream = document.getElementById('stream-toggle').checked;
  const contextLimit = parseInt(document.getElementById('context-limit').value) || 200000;
  const saveBtn = document.getElementById('save-settings-btn');
  const validationMsg = document.getElementById('validation-message');

  if (!model) {
    validationMsg.textContent = 'Please select a model';
    validationMsg.className = 'validation-message error';
    return;
  }

  saveBtn.disabled = true;
  saveBtn.classList.add('loading');
  saveBtn.textContent = 'Saving...';
  setStatus('Saving configuration...', 'default');

  try {
    await invoke('save_api_config', { baseUrl, apiKey, model, stream, contextLimit });
    validationMsg.textContent = 'Configuration saved successfully';
    validationMsg.className = 'validation-message success';
    setStatus('Configuration saved', 'success');

    setTimeout(() => {
      hideSettings();
      messagesContainer.innerHTML = '';
      addMessage('API configured. Ready to chat.', false, true);
      setStatus('Ready');
    }, 1000);
  } catch (error) {
    validationMsg.textContent = `Failed to save: ${error}`;
    validationMsg.className = 'validation-message error';
    setStatus('Failed to save configuration', 'error');
  } finally {
    saveBtn.disabled = false;
    saveBtn.classList.remove('loading');
    saveBtn.textContent = 'Save Configuration';
  }
}

// Avatar upload handling
async function handleAvatarUpload() {
  const characterMsg = document.getElementById('character-message');

  try {
    const characterId = document.getElementById('character-settings-select').value;
    const avatarFilename = await invoke('select_and_upload_avatar', {
      characterId: characterId
    });

    pendingAvatarPath = avatarFilename;

    // Update preview
    const avatarPreview = document.querySelector('.avatar-circle-large');
    const avatarUrl = await getAvatarUrl(avatarFilename);
    if (avatarUrl) {
      avatarPreview.style.backgroundImage = `url('${avatarUrl}')`;
    }
    document.getElementById('remove-avatar-btn').style.display = 'inline-block';

    characterMsg.textContent = 'Avatar uploaded. Click "Save Character" to apply.';
    characterMsg.className = 'validation-message success';
    setTimeout(() => {
      characterMsg.style.display = 'none';
    }, 3000);
  } catch (error) {
    console.error('Avatar upload error:', error);
    // Don't show error if user just cancelled the dialog
    if (error && !error.toString().includes('No file selected')) {
      characterMsg.textContent = `Failed to upload avatar: ${error}`;
      characterMsg.className = 'validation-message error';
    }
  }
}

function handleAvatarRemove() {
  pendingAvatarPath = null;
  const avatarPreview = document.querySelector('.avatar-circle-large');
  avatarPreview.style.backgroundImage = '';
  document.getElementById('remove-avatar-btn').style.display = 'none';

  const characterMsg = document.getElementById('character-message');
  characterMsg.textContent = 'Avatar removed. Click "Save Character" to apply.';
  characterMsg.className = 'validation-message success';
  setTimeout(() => {
    characterMsg.style.display = 'none';
  }, 3000);
}

// App controls
function setupAppControls() {
  document.getElementById('settings-btn').addEventListener('click', showSettings);
  document.getElementById('close-settings-btn').addEventListener('click', hideSettings);
  document.getElementById('settings-overlay').addEventListener('click', hideSettings);
  document.getElementById('roleplay-btn').addEventListener('click', showRoleplayPanel);
  document.getElementById('close-roleplay-btn').addEventListener('click', hideRoleplayPanel);
  document.getElementById('roleplay-overlay').addEventListener('click', hideRoleplayPanel);
  document.getElementById('clear-btn').addEventListener('click', clearHistory);
  document.getElementById('export-chat-btn').addEventListener('click', exportChatHistory);
  document.getElementById('import-chat-btn').addEventListener('click', importChatHistory);
  characterSelect.addEventListener('change', handleCharacterSwitch);
  newCharacterBtn.addEventListener('click', handleNewCharacter);
  document.getElementById('delete-character-btn').addEventListener('click', handleDeleteCharacter);
  document.getElementById('character-settings-select').addEventListener('change', async () => {
    const characterId = document.getElementById('character-settings-select').value;
    await invoke('set_active_character', { characterId });
    await loadCharacterSettings();
  });
  document.getElementById('upload-avatar-btn').addEventListener('click', handleAvatarUpload);
  document.getElementById('remove-avatar-btn').addEventListener('click', handleAvatarRemove);
  document.getElementById('import-character-btn').addEventListener('click', handleImportCharacter);
  document.getElementById('export-character-btn').addEventListener('click', handleExportCharacter);

  // Setup collapsible sections
  document.querySelectorAll('.settings-section-header').forEach(header => {
    header.addEventListener('click', () => {
      const section = header.parentElement;
      section.classList.toggle('collapsed');
    });
  });

  // Setup theme selector
  const themeSelect = document.getElementById('theme-select');
  if (themeSelect) {
    themeSelect.addEventListener('change', (e) => {
      applyTheme(e.target.value);
    });
  }

  // Setup view mode selector
  const viewModeSelect = document.getElementById('view-mode-select');
  if (viewModeSelect) {
    viewModeSelect.addEventListener('change', (e) => {
      applyViewMode(e.target.value);
    });
  }

  // Setup font size slider
  const fontSizeSlider = document.getElementById('font-size-slider');
  if (fontSizeSlider) {
    fontSizeSlider.addEventListener('input', (e) => {
      applyFontSize(parseInt(e.target.value));
    });
  }

  // Setup roleplay panel buttons
  document.getElementById('add-worldinfo-btn').addEventListener('click', handleAddWorldInfoEntry);
  document.getElementById('save-authors-note-btn').addEventListener('click', handleSaveAuthorsNote);
  document.getElementById('save-persona-btn').addEventListener('click', handleSavePersona);
  document.getElementById('save-examples-btn').addEventListener('click', handleSaveExamples);

  // Setup recursion depth change handler
  document.getElementById('recursion-depth').addEventListener('change', handleRecursionDepthChange);

  // Setup preset controls
  document.getElementById('preset-select').addEventListener('change', (e) => {
    handlePresetSelect(e.target.value);
  });
  document.getElementById('apply-preset-btn').addEventListener('click', handleApplyPreset);
  document.getElementById('create-preset-btn').addEventListener('click', handleCreatePreset);
  document.getElementById('add-instruction-btn').addEventListener('click', addInstructionBlock);
  document.getElementById('save-preset-changes-btn').addEventListener('click', savePresetChanges);
  document.getElementById('delete-preset-btn').addEventListener('click', deletePreset);
  document.getElementById('duplicate-preset-btn').addEventListener('click', duplicatePreset);
  document.getElementById('restore-preset-btn').addEventListener('click', restoreBuiltinPreset);
}

// Keyboard shortcuts
function setupKeyboardShortcuts() {
  messageInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSubmit(e);
    }
  });

  messageInput.addEventListener('input', () => {
    autoResize(messageInput);
    updateTokenCount();
  });
}

// Token Counter
let tokenUpdateTimeout = null;

// Helper function to format token counts
function formatTokenCount(count) {
  if (count >= 1000) {
    return (count / 1000).toFixed(1) + 'k';
  }
  return count.toString();
}

async function updateTokenCount() {
  // Debounce token count updates
  if (tokenUpdateTimeout) {
    clearTimeout(tokenUpdateTimeout);
  }

  tokenUpdateTimeout = setTimeout(async () => {
    try {
      const currentInput = messageInput.value;
      const tokenData = await invoke('get_token_count', {
        characterId: null, // Use active character
        currentInput
      });

      // Get context limit from config
      let contextLimit = 200000; // Default
      try {
        const config = await invoke('get_api_config');
        contextLimit = config.context_limit || 200000;
      } catch (e) {
        // Use default if config not available
      }

      // Update total display
      const tokenCounter = document.getElementById('token-counter');
      const tokenCountTotal = document.getElementById('token-count-total');

      // Format: "2.5k / 200k tokens"
      tokenCountTotal.textContent = `${formatTokenCount(tokenData.total)} / ${formatTokenCount(contextLimit)} tokens`;

      // Update breakdown
      document.getElementById('token-system').textContent = tokenData.system_prompt;
      document.getElementById('token-preset').textContent = tokenData.preset_instructions;
      document.getElementById('token-persona').textContent = tokenData.persona;
      document.getElementById('token-worldinfo').textContent = tokenData.world_info;
      document.getElementById('token-authorsnote').textContent = tokenData.authors_note;
      document.getElementById('token-examples').textContent = tokenData.message_examples;
      document.getElementById('token-history').textContent = tokenData.message_history;
      document.getElementById('token-input').textContent = tokenData.current_input;
      document.getElementById('token-total-detail').textContent = tokenData.total;
    } catch (error) {
      console.error('Failed to update token count:', error);
      // Keep counter visible, just show 0
      const tokenCountTotal = document.getElementById('token-count-total');
      tokenCountTotal.textContent = '0 / 200k tokens';
    }
  }, 300); // Update after 300ms of no typing
}

// Toggle token breakdown display
document.getElementById('token-details-btn').addEventListener('click', (e) => {
  e.stopPropagation();
  const breakdown = document.getElementById('token-breakdown');
  breakdown.style.display = breakdown.style.display === 'none' ? 'block' : 'none';
});

// Close breakdown when clicking outside
document.addEventListener('click', (e) => {
  const breakdown = document.getElementById('token-breakdown');
  const detailsBtn = document.getElementById('token-details-btn');

  if (!breakdown.contains(e.target) && !detailsBtn.contains(e.target)) {
    breakdown.style.display = 'none';
  }
});

// Load characters and populate dropdown
async function loadCharacters() {
  console.log('Loading characters...');
  try {
    const characters = await invoke('list_characters');
    console.log('Loaded characters:', characters);
    characterSelect.innerHTML = '';
    characters.forEach(char => {
      const option = document.createElement('option');
      option.value = char.id;
      option.textContent = char.name;
      characterSelect.appendChild(option);
    });

    const activeCharacter = await invoke('get_character');
    console.log('Active character:', activeCharacter);
    characterSelect.value = activeCharacter.id;
    characterHeaderName.textContent = activeCharacter.name;
    currentCharacter = activeCharacter;

    // Update header avatar
    const headerAvatar = document.querySelector('.avatar-circle');
    if (headerAvatar && activeCharacter.avatar_path) {
      getAvatarUrl(activeCharacter.avatar_path).then(url => {
        if (url) {
          headerAvatar.style.backgroundImage = `url('${url}')`;
          makeAvatarClickable(headerAvatar, url);
        }
      });
    } else if (headerAvatar) {
      headerAvatar.style.backgroundImage = '';
    }

    await loadChatHistory();
  } catch (error) {
    console.error('Failed to load characters:', error);
    addMessage(`Failed to load characters: ${error}`, false);
  }
}

// Handle character switching
async function handleCharacterSwitch() {
  const characterId = characterSelect.value;
  setStatus('Switching character...', 'default');
  try {
    await invoke('set_active_character', { characterId });
    messagesContainer.innerHTML = '';
    await loadCharacters();
    setStatus('Character switched', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to switch character:', error);
    setStatus('Failed to switch character', 'error');
    addMessage(`Failed to switch character: ${error}`, false);
  }
}

// Handle new character creation
async function handleNewCharacter() {
  const modal = document.getElementById('new-character-modal');
  const overlay = modal.querySelector('.new-character-overlay');
  const form = document.getElementById('new-character-form');
  const nameInput = document.getElementById('new-character-name');
  const systemPromptInput = document.getElementById('new-character-system-prompt');
  const closeBtn = document.getElementById('close-new-character-btn');
  const cancelBtn = document.getElementById('cancel-new-character-btn');

  // Reset form
  form.reset();
  systemPromptInput.value = 'You are a helpful AI assistant.';

  // Show modal
  modal.style.display = 'flex';

  // Focus name input after a brief delay to ensure it's visible
  setTimeout(() => nameInput.focus(), 100);

  // Handle form submission
  const handleSubmit = async (e) => {
    e.preventDefault();

    const name = nameInput.value.trim();
    const systemPrompt = systemPromptInput.value.trim();

    if (!name || !systemPrompt) return;

    try {
      const newCharacter = await invoke('create_character', { name, systemPrompt });
      await loadCharacters();
      characterSelect.value = newCharacter.id;

      // Hide modal
      modal.style.display = 'none';

      // Remove event listeners
      form.removeEventListener('submit', handleSubmit);
      overlay.removeEventListener('click', handleClose);
      closeBtn.removeEventListener('click', handleClose);
      cancelBtn.removeEventListener('click', handleClose);
    } catch (error) {
      console.error('Failed to create character:', error);
      addMessage(`Failed to create character: ${error}`, false);
    }
  };

  // Handle close
  const handleClose = () => {
    modal.style.display = 'none';
    form.removeEventListener('submit', handleSubmit);
    overlay.removeEventListener('click', handleClose);
    closeBtn.removeEventListener('click', handleClose);
    cancelBtn.removeEventListener('click', handleClose);
  };

  // Attach event listeners
  form.addEventListener('submit', handleSubmit);
  overlay.addEventListener('click', handleClose);
  closeBtn.addEventListener('click', handleClose);
  cancelBtn.addEventListener('click', handleClose);
}

// Handle character deletion
async function handleDeleteCharacter() {
  if (!currentCharacter || currentCharacter.id === 'default') {
    addMessage('Cannot delete the default character.', false);
    return;
  }

  if (confirm(`Are you sure you want to delete ${currentCharacter.name}? This cannot be undone.`)) {
    try {
      await invoke('delete_character', { characterId: currentCharacter.id });
      await loadCharacters();
      hideSettings();
    } catch (error) {
      console.error('Failed to delete character:', error);
      addMessage(`Failed to delete character: ${error}`, false);
    }
  }
}

// Handle character card import
async function handleImportCharacter() {
  const characterMsg = document.getElementById('character-message');
  try {
    const importedCharacter = await invoke('import_character_card');
    characterMsg.textContent = `Successfully imported ${importedCharacter.name}!`;
    characterMsg.className = 'validation-message success';

    // Reload characters and switch to the imported one
    await loadCharacters();
    await loadCharacterSettings();

    setTimeout(() => {
      characterMsg.style.display = 'none';
    }, 3000);
  } catch (error) {
    console.error('Failed to import character:', error);
    if (error && !error.toString().includes('No file selected') && !error.toString().includes('cancelled')) {
      characterMsg.textContent = `Failed to import: ${error}`;
      characterMsg.className = 'validation-message error';
    }
  }
}

// Handle character card export
async function handleExportCharacter() {
  const characterMsg = document.getElementById('character-message');
  try {
    const characterId = document.getElementById('character-settings-select').value;
    const outputPath = await invoke('export_character_card', { characterId });
    characterMsg.textContent = `Successfully exported to ${outputPath}`;
    characterMsg.className = 'validation-message success';

    setTimeout(() => {
      characterMsg.style.display = 'none';
    }, 3000);
  } catch (error) {
    console.error('Failed to export character:', error);
    if (error && !error.toString().includes('cancelled')) {
      characterMsg.textContent = `Failed to export: ${error}`;
      characterMsg.className = 'validation-message error';
    }
  }
}

// Load chat history
async function loadChatHistory() {
  try {
    const history = await invoke('get_chat_history');
    messagesContainer.innerHTML = '';

    if (history.length === 0) {
      if (currentCharacter && currentCharacter.greeting) {
        addMessage(currentCharacter.greeting, false, true);
      } else {
        addMessage('API configured. Ready to chat.', false, true);
      }
    } else {
      history.forEach((msg, index) => {
        const messageDiv = addMessage(msg.content, msg.role === 'user', false, msg.timestamp);

        // Apply pinned state
        if (msg.pinned && messageDiv) {
          messageDiv.classList.add('pinned');
          const pinBtn = messageDiv.querySelector('.message-pin-btn');
          if (pinBtn) {
            pinBtn.classList.add('active');
            pinBtn.title = 'Unpin message';
          }
        }

        // Apply hidden state
        if (msg.hidden && messageDiv) {
          messageDiv.classList.add('hidden-message');
          const hideBtn = messageDiv.querySelector('.message-hide-btn');
          if (hideBtn) {
            hideBtn.classList.add('active');
            hideBtn.title = 'Unhide message';
            hideBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
              <path d="M10 5L11.5 3.5M3.5 10.5L5 9M1 13L13 1M5.5 6C5.19 6.31 5 6.74 5 7.22C5 8.2 5.8 9 6.78 9C7.26 9 7.69 8.81 8 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
            </svg>`;
          }
        }

        // Update swipe controls for assistant messages with swipe info
        if (msg.role === 'assistant' && messageDiv && msg.swipes && msg.swipes.length > 0) {
          updateSwipeControls(messageDiv, msg.current_swipe || 0, msg.swipes.length);
        }
      });
    }
  } catch (error) {
    console.error('Failed to load chat history:', error);
    messagesContainer.innerHTML = '';
    addMessage('API configured. Ready to chat.', false, true);
  }

  // Update token count after loading history
  updateTokenCount();
}

// Clear chat history
async function clearHistory() {
  if (!confirm('Clear conversation history? This cannot be undone.')) {
    return;
  }

  setStatus('Clearing history...', 'default');
  try {
    await invoke('clear_chat_history');
    messagesContainer.innerHTML = '';
    if (currentCharacter && currentCharacter.greeting) {
      addMessage(currentCharacter.greeting, false, true);
    } else {
      addMessage('Conversation cleared. Ready to chat.', false, true);
    }
    setStatus('History cleared', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    setStatus('Failed to clear history', 'error');
    addMessage(`Failed to clear history: ${error}`, false);
  }
}

// Load character settings
async function loadCharacterSettings() {
  try {
    const characters = await invoke('list_characters');
    const characterSettingsSelect = document.getElementById('character-settings-select');
    characterSettingsSelect.innerHTML = '';
    characters.forEach(char => {
      const option = document.createElement('option');
      option.value = char.id;
      option.textContent = char.name;
      characterSettingsSelect.appendChild(option);
    });

    const character = await invoke('get_character');
    characterSettingsSelect.value = character.id;
    document.getElementById('character-name').value = character.name;
    document.getElementById('character-system-prompt').value = character.system_prompt;
    document.getElementById('character-greeting').value = character.greeting || '';
    document.getElementById('character-personality').value = character.personality || '';
    document.getElementById('character-description').value = character.description || '';
    document.getElementById('character-scenario').value = character.scenario || '';
    document.getElementById('character-mes-example').value = character.mes_example || '';
    document.getElementById('character-post-history').value = character.post_history_instructions || '';
    document.getElementById('character-alt-greetings').value = character.alternate_greetings ? character.alternate_greetings.join('\n') : '';
    document.getElementById('character-tags').value = character.tags ? character.tags.join(', ') : '';
    document.getElementById('character-creator').value = character.creator || '';
    document.getElementById('character-version').value = character.character_version || '';
    document.getElementById('character-creator-notes').value = character.creator_notes || '';

    // Load avatar preview
    const avatarPreview = document.querySelector('.avatar-circle-large');
    const removeAvatarBtn = document.getElementById('remove-avatar-btn');
    if (character.avatar_path) {
      getAvatarUrl(character.avatar_path).then(url => {
        if (url) {
          avatarPreview.style.backgroundImage = `url('${url}')`;
          makeAvatarClickable(avatarPreview, url);
        }
      });
      removeAvatarBtn.style.display = 'inline-block';
      pendingAvatarPath = character.avatar_path;
    } else {
      avatarPreview.style.backgroundImage = '';
      removeAvatarBtn.style.display = 'none';
      pendingAvatarPath = null;
    }
  } catch (error) {
    console.error('Failed to load character:', error);
  }
}

// Save character settings
async function handleSaveCharacter(e) {
  e.preventDefault();

  const name = document.getElementById('character-name').value.trim();
  const systemPrompt = document.getElementById('character-system-prompt').value.trim();
  const greeting = document.getElementById('character-greeting').value.trim() || null;
  const personality = document.getElementById('character-personality').value.trim() || null;
  const description = document.getElementById('character-description').value.trim() || null;
  const scenario = document.getElementById('character-scenario').value.trim() || null;
  const mesExample = document.getElementById('character-mes-example').value.trim() || null;
  const postHistory = document.getElementById('character-post-history').value.trim() || null;
  const altGreetingsText = document.getElementById('character-alt-greetings').value.trim();
  const altGreetings = altGreetingsText ? altGreetingsText.split('\n').map(s => s.trim()).filter(s => s) : null;
  const tagsText = document.getElementById('character-tags').value.trim();
  const tags = tagsText ? tagsText.split(',').map(s => s.trim()).filter(s => s) : null;
  const creator = document.getElementById('character-creator').value.trim() || null;
  const characterVersion = document.getElementById('character-version').value.trim() || null;
  const creatorNotes = document.getElementById('character-creator-notes').value.trim() || null;
  const saveBtn = document.getElementById('save-character-btn');
  const characterMsg = document.getElementById('character-message');

  if (!name || !systemPrompt) {
    characterMsg.textContent = 'Name and System Prompt are required';
    characterMsg.className = 'validation-message error';
    return;
  }

  saveBtn.disabled = true;
  saveBtn.classList.add('loading');
  saveBtn.textContent = 'Saving...';

  try {
    await invoke('update_character', {
      name,
      systemPrompt,
      greeting,
      personality,
      description,
      scenario,
      mesExample,
      postHistory,
      altGreetings,
      tags,
      creator,
      characterVersion,
      creatorNotes,
      avatarPath: pendingAvatarPath
    });
    characterMsg.textContent = 'Character saved successfully';
    characterMsg.className = 'validation-message success';

    await loadCharacters();

    setTimeout(() => {
      characterMsg.style.display = 'none';
    }, 3000);
  } catch (error) {
    characterMsg.textContent = `Failed to save: ${error}`;
    characterMsg.className = 'validation-message error';
  } finally {
    saveBtn.disabled = false;
    saveBtn.classList.remove('loading');
    saveBtn.textContent = 'Save Character';
  }
}

// World Info / Roleplay Settings Management

let currentRoleplaySettings = null;

// Load roleplay settings for current character
async function loadRoleplaySettings() {
  if (!currentCharacter) return;

  try {
    const settings = await invoke('get_roleplay_settings', { characterId: currentCharacter.id });
    currentRoleplaySettings = settings;

    // Load World Info entries
    renderWorldInfoList(settings.world_info || []);

    // Load World Info recursion depth
    document.getElementById('recursion-depth').value = settings.recursion_depth || 3;

    // Load Author's Note
    document.getElementById('authors-note-text').value = settings.authors_note || '';
    document.getElementById('authors-note-enabled').checked = settings.authors_note_enabled || false;

    // Load Persona
    document.getElementById('persona-name').value = settings.persona_name || '';
    document.getElementById('persona-description').value = settings.persona_description || '';
    document.getElementById('persona-enabled').checked = settings.persona_enabled || false;

    // Load Message Examples
    document.getElementById('examples-enabled').checked = settings.examples_enabled || false;
    document.getElementById('examples-position').value = settings.examples_position || 'after_system';

    // Load Presets
    await loadPresets();

    // Update feature badges
    updateFeatureBadges();
  } catch (error) {
    console.error('Failed to load roleplay settings:', error);
  }
}

// Update feature badges in header
function updateFeatureBadges() {
  const badgesContainer = document.getElementById('feature-badges');
  if (!badgesContainer || !currentRoleplaySettings) {
    if (badgesContainer) badgesContainer.innerHTML = '';
    return;
  }

  badgesContainer.innerHTML = '';

  // World Info badge - show count of enabled entries
  const worldInfoEntries = (currentRoleplaySettings.world_info || []).filter(entry => entry.enabled);
  if (worldInfoEntries.length > 0) {
    const badge = document.createElement('div');
    badge.className = 'feature-badge';
    badge.title = `${worldInfoEntries.length} World Info ${worldInfoEntries.length === 1 ? 'entry' : 'entries'} active`;
    badge.innerHTML = `
      <svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
        <path d="M2 4h12M2 8h12M2 12h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>
      <span>WI: <span class="feature-badge-count">${worldInfoEntries.length}</span></span>
    `;
    badgesContainer.appendChild(badge);
  }

  // Persona badge
  if (currentRoleplaySettings.persona_enabled && currentRoleplaySettings.persona_name) {
    const badge = document.createElement('div');
    badge.className = 'feature-badge';
    badge.title = `Persona: ${currentRoleplaySettings.persona_name}`;
    badge.innerHTML = `
      <svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
        <circle cx="8" cy="5" r="2.5" stroke="currentColor" stroke-width="1.5"/>
        <path d="M3 13c0-2.5 2-4 5-4s5 1.5 5 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>
      <span>${currentRoleplaySettings.persona_name}</span>
    `;
    badgesContainer.appendChild(badge);
  }

  // Preset badge
  const presetSelect = document.getElementById('preset-select');
  if (presetSelect && presetSelect.value) {
    const presetName = presetSelect.options[presetSelect.selectedIndex]?.text || presetSelect.value;
    const badge = document.createElement('div');
    badge.className = 'feature-badge';
    badge.title = `Active Preset: ${presetName}`;
    badge.innerHTML = `
      <svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
        <rect x="3" y="3" width="10" height="10" rx="1" stroke="currentColor" stroke-width="1.5"/>
        <path d="M6 7h4M6 9h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>
      <span>${presetName}</span>
    `;
    badgesContainer.appendChild(badge);
  }

  // Message Examples badge
  if (currentRoleplaySettings.examples_enabled) {
    const badge = document.createElement('div');
    badge.className = 'feature-badge';
    badge.title = 'Message Examples enabled';
    badge.innerHTML = `
      <svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
        <path d="M3 6l2 2-2 2M7 10h5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
      <span>Examples</span>
    `;
    badgesContainer.appendChild(badge);
  }

  // Author's Note badge
  if (currentRoleplaySettings.authors_note_enabled && currentRoleplaySettings.authors_note) {
    const badge = document.createElement('div');
    badge.className = 'feature-badge';
    badge.title = "Author's Note enabled";
    badge.innerHTML = `
      <svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
        <path d="M3 3h10v10H3z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
        <path d="M6 6h4M6 9h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
      </svg>
      <span>A/N</span>
    `;
    badgesContainer.appendChild(badge);
  }
}

// Render World Info entries
function renderWorldInfoList(entries) {
  const listContainer = document.getElementById('worldinfo-list');
  listContainer.innerHTML = '';

  if (entries.length === 0) {
    const emptyMsg = document.createElement('p');
    emptyMsg.style.color = 'var(--text-secondary)';
    emptyMsg.style.fontSize = '14px';
    emptyMsg.style.textAlign = 'center';
    emptyMsg.style.padding = '20px';
    emptyMsg.textContent = 'No entries yet. Click "Add Entry" to create one.';
    listContainer.appendChild(emptyMsg);
    return;
  }

  // Sort entries by priority (higher first)
  const sortedEntries = [...entries].sort((a, b) => (b.priority || 0) - (a.priority || 0));

  sortedEntries.forEach(entry => {
    const entryDiv = document.createElement('div');
    entryDiv.className = 'worldinfo-entry';
    entryDiv.dataset.entryId = entry.id;

    const header = document.createElement('div');
    header.className = 'worldinfo-entry-header';

    const enableCheckbox = document.createElement('input');
    enableCheckbox.type = 'checkbox';
    enableCheckbox.checked = entry.enabled;
    enableCheckbox.addEventListener('change', () => handleToggleWorldInfoEntry(entry.id, enableCheckbox.checked));

    const keysText = document.createElement('span');
    keysText.className = 'worldinfo-keys';
    keysText.textContent = entry.keys.join(', ');

    const priority = document.createElement('span');
    priority.className = 'worldinfo-priority';
    priority.textContent = `Priority: ${entry.priority || 0}`;

    const actionsDiv = document.createElement('div');
    actionsDiv.className = 'worldinfo-entry-actions';

    const editBtn = document.createElement('button');
    editBtn.className = 'worldinfo-btn';
    editBtn.textContent = 'Edit';
    editBtn.addEventListener('click', () => handleEditWorldInfoEntry(entry));

    const deleteBtn = document.createElement('button');
    deleteBtn.className = 'worldinfo-btn worldinfo-btn-danger';
    deleteBtn.textContent = 'Delete';
    deleteBtn.addEventListener('click', () => handleDeleteWorldInfoEntry(entry.id));

    actionsDiv.appendChild(editBtn);
    actionsDiv.appendChild(deleteBtn);

    header.appendChild(enableCheckbox);
    header.appendChild(keysText);
    header.appendChild(priority);
    header.appendChild(actionsDiv);

    const content = document.createElement('div');
    content.className = 'worldinfo-entry-content';
    content.textContent = entry.content;

    entryDiv.appendChild(header);
    entryDiv.appendChild(content);
    listContainer.appendChild(entryDiv);
  });
}

// Add new World Info entry
async function handleAddWorldInfoEntry() {
  const listContainer = document.getElementById('worldinfo-list');

  // Check if form already exists
  if (document.getElementById('worldinfo-add-form')) return;

  // Create inline form
  const formDiv = document.createElement('div');
  formDiv.id = 'worldinfo-add-form';
  formDiv.className = 'worldinfo-entry worldinfo-edit-form';

  formDiv.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 12px;">
      <div style="font-weight: 600; color: var(--text-primary); margin-bottom: 4px;">Add World Info Entry</div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Keywords (comma-separated)</label>
        <input type="text" id="wi-add-keys" placeholder="John, John Smith" style="width: 100%;" />
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Content</label>
        <textarea id="wi-add-content" placeholder="Information to inject when keywords are found..." rows="4" style="width: 100%;"></textarea>
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Priority</label>
        <input type="number" id="wi-add-priority" value="0" min="0" style="width: 100px;" />
      </div>
      <div style="display: flex; gap: 8px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn" id="wi-add-cancel">Cancel</button>
        <button type="button" class="worldinfo-btn" id="wi-add-save" style="background: var(--accent); color: white;">Save</button>
      </div>
    </div>
  `;

  listContainer.prepend(formDiv);

  // Focus first input
  document.getElementById('wi-add-keys').focus();

  // Handle cancel
  document.getElementById('wi-add-cancel').addEventListener('click', () => {
    formDiv.remove();
  });

  // Handle save
  document.getElementById('wi-add-save').addEventListener('click', async () => {
    const keys = document.getElementById('wi-add-keys').value.trim();
    const content = document.getElementById('wi-add-content').value.trim();
    const priority = parseInt(document.getElementById('wi-add-priority').value) || 0;

    if (!keys || !content) {
      alert('Keywords and content are required');
      return;
    }

    try {
      const keysArray = keys.split(',').map(k => k.trim()).filter(k => k);
      await invoke('add_world_info_entry', {
        characterId: currentCharacter.id,
        keys: keysArray,
        content: content,
        priority,
        caseSensitive: false
      });

      formDiv.remove();
      await loadRoleplaySettings();
    } catch (error) {
      console.error('Failed to add World Info entry:', error);
      alert(`Failed to add entry: ${error}`);
    }
  });
}

// Edit World Info entry
async function handleEditWorldInfoEntry(entry) {
  const entryDiv = document.querySelector(`.worldinfo-entry[data-entry-id="${entry.id}"]`);
  if (!entryDiv) return;

  // Check if already editing
  if (entryDiv.querySelector('.worldinfo-inline-edit')) return;

  // Hide normal content
  const header = entryDiv.querySelector('.worldinfo-entry-header');
  const content = entryDiv.querySelector('.worldinfo-entry-content');
  header.style.display = 'none';
  content.style.display = 'none';

  // Create inline edit form
  const editForm = document.createElement('div');
  editForm.className = 'worldinfo-inline-edit';
  editForm.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 12px;">
      <div style="font-weight: 600; color: var(--text-primary); margin-bottom: 4px;">Edit Entry</div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Keywords (comma-separated)</label>
        <input type="text" class="wi-edit-keys" value="${entry.keys.join(', ')}" style="width: 100%;" />
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Content</label>
        <textarea class="wi-edit-content" rows="4" style="width: 100%;">${entry.content}</textarea>
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Priority</label>
        <input type="number" class="wi-edit-priority" value="${entry.priority || 0}" min="0" style="width: 100px;" />
      </div>
      <div style="display: flex; gap: 8px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn wi-edit-cancel">Cancel</button>
        <button type="button" class="worldinfo-btn wi-edit-save" style="background: var(--accent); color: white;">Save</button>
      </div>
    </div>
  `;

  entryDiv.appendChild(editForm);

  // Focus first input
  editForm.querySelector('.wi-edit-keys').focus();

  // Handle cancel
  editForm.querySelector('.wi-edit-cancel').addEventListener('click', () => {
    header.style.display = '';
    content.style.display = '';
    editForm.remove();
  });

  // Handle save
  editForm.querySelector('.wi-edit-save').addEventListener('click', async () => {
    const keys = editForm.querySelector('.wi-edit-keys').value.trim();
    const contentText = editForm.querySelector('.wi-edit-content').value.trim();
    const priority = parseInt(editForm.querySelector('.wi-edit-priority').value) || 0;

    if (!keys || !contentText) {
      alert('Keywords and content are required');
      return;
    }

    try {
      const keysArray = keys.split(',').map(k => k.trim()).filter(k => k);
      await invoke('update_world_info_entry', {
        characterId: currentCharacter.id,
        entryId: entry.id,
        keys: keysArray,
        content: contentText,
        enabled: entry.enabled,
        priority,
        caseSensitive: entry.case_sensitive
      });

      await loadRoleplaySettings();
    } catch (error) {
      console.error('Failed to update World Info entry:', error);
      alert(`Failed to update entry: ${error}`);
    }
  });
}

// Toggle World Info entry enabled state
async function handleToggleWorldInfoEntry(entryId, enabled) {
  if (!currentRoleplaySettings) return;

  const entry = currentRoleplaySettings.world_info.find(e => e.id === entryId);
  if (!entry) return;

  try {
    await invoke('update_world_info_entry', {
      characterId: currentCharacter.id,
      entryId: entryId,
      keys: entry.keys,
      content: entry.content,
      enabled: enabled,
      priority: entry.priority,
      caseSensitive: entry.case_sensitive
    });

    // Update local settings
    entry.enabled = enabled;

    // Update feature badges
    updateFeatureBadges();
  } catch (error) {
    console.error('Failed to toggle World Info entry:', error);
    alert(`Failed to toggle entry: ${error}`);
  }
}

// Delete World Info entry
async function handleDeleteWorldInfoEntry(entryId) {
  if (!confirm('Delete this World Info entry? This cannot be undone.')) return;

  try {
    await invoke('delete_world_info_entry', {
      characterId: currentCharacter.id,
      entryId: entryId
    });

    // Reload settings
    await loadRoleplaySettings();
  } catch (error) {
    console.error('Failed to delete World Info entry:', error);
    alert(`Failed to delete entry: ${error}`);
  }
}

// Save Author's Note
async function handleSaveAuthorsNote() {
  if (!currentCharacter) return;

  const content = document.getElementById('authors-note-text').value.trim() || null;
  const enabled = document.getElementById('authors-note-enabled').checked;

  try {
    await invoke('update_authors_note', {
      characterId: currentCharacter.id,
      content,
      enabled
    });

    // Update currentRoleplaySettings
    if (currentRoleplaySettings) {
      currentRoleplaySettings.authors_note = content;
      currentRoleplaySettings.authors_note_enabled = enabled;
    }

    // Update feature badges
    updateFeatureBadges();

    // Show success message
    setStatus('Author\'s Note saved', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to save Author\'s Note:', error);
    setStatus('Failed to save Author\'s Note', 'error');
  }
}

// Save Persona
async function handleSavePersona() {
  if (!currentCharacter) return;

  const name = document.getElementById('persona-name').value.trim() || null;
  const description = document.getElementById('persona-description').value.trim() || null;
  const enabled = document.getElementById('persona-enabled').checked;

  try {
    await invoke('update_persona', {
      characterId: currentCharacter.id,
      name,
      description,
      enabled
    });

    // Update currentRoleplaySettings
    if (currentRoleplaySettings) {
      currentRoleplaySettings.persona_name = name;
      currentRoleplaySettings.persona_description = description;
      currentRoleplaySettings.persona_enabled = enabled;
    }

    // Update feature badges
    updateFeatureBadges();

    // Show success message
    setStatus('Persona saved', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to save Persona:', error);
    setStatus('Failed to save Persona', 'error');
  }
}

// Save Message Examples Settings
async function handleSaveExamples() {
  if (!currentCharacter) return;

  const enabled = document.getElementById('examples-enabled').checked;
  const position = document.getElementById('examples-position').value;

  try {
    await invoke('update_examples_settings', {
      characterId: currentCharacter.id,
      enabled,
      position
    });

    // Update currentRoleplaySettings
    if (currentRoleplaySettings) {
      currentRoleplaySettings.examples_enabled = enabled;
      currentRoleplaySettings.examples_position = position;
    }

    // Update feature badges
    updateFeatureBadges();

    // Show success message
    setStatus('Message Examples settings saved', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to save Message Examples settings:', error);
    setStatus('Failed to save Message Examples settings', 'error');
  }
}

// Handle recursion depth change
async function handleRecursionDepthChange() {
  if (!currentCharacter) return;

  const depth = parseInt(document.getElementById('recursion-depth').value) || 3;

  try {
    await invoke('update_recursion_depth', {
      characterId: currentCharacter.id,
      depth
    });

    console.log('Recursion depth updated to:', depth);
  } catch (error) {
    console.error('Failed to update recursion depth:', error);
    setStatus('Failed to save recursion depth', 'error');
    setTimeout(() => setStatus('Ready'), 2000);
  }
}

// Prompt Preset Management

// Load available presets
async function loadPresets() {
  try {
    const presets = await invoke('get_presets');
    const presetSelect = document.getElementById('preset-select');

    // Clear existing options except "No Preset"
    presetSelect.innerHTML = '<option value="">No Preset</option>';

    // Add presets to dropdown
    presets.forEach(preset => {
      const option = document.createElement('option');
      option.value = preset.id;
      option.textContent = preset.name;
      presetSelect.appendChild(option);
    });

    // Set current preset if one is active
    if (currentRoleplaySettings && currentRoleplaySettings.active_preset_id) {
      presetSelect.value = currentRoleplaySettings.active_preset_id;
      await handlePresetSelect(currentRoleplaySettings.active_preset_id);
    } else {
      presetSelect.value = '';
      hidePresetInfo();
    }
  } catch (error) {
    console.error('Failed to load presets:', error);
  }
}

// Hide preset info panel
function hidePresetInfo() {
  const presetInfo = document.getElementById('preset-info');
  const applyBtn = document.getElementById('apply-preset-btn');
  presetInfo.style.display = 'none';
  applyBtn.disabled = true;
}

// Global variable to track current preset being edited
let currentEditingPreset = null;

// Show preset details/editor
async function handlePresetSelect(presetId) {
  if (!presetId) {
    hidePresetInfo();
    return;
  }

  try {
    const preset = await invoke('get_preset', { presetId });
    currentEditingPreset = preset;

    // Determine if this is a built-in preset
    const builtInIds = ['default', 'roleplay', 'creative-writing', 'assistant'];
    const isBuiltIn = builtInIds.includes(preset.id);

    // Show preset info
    const presetInfo = document.getElementById('preset-info');
    const presetName = document.getElementById('preset-name');
    const presetDescription = document.getElementById('preset-description');
    const builtInBadge = document.getElementById('preset-builtin-badge');
    const deleteBtn = document.getElementById('delete-preset-btn');
    const duplicateBtn = document.getElementById('duplicate-preset-btn');
    const saveChangesBtn = document.getElementById('save-preset-changes-btn');
    const addInstructionBtn = document.getElementById('add-instruction-btn');
    const applyBtn = document.getElementById('apply-preset-btn');

    // System additions elements
    const systemReadonly = document.getElementById('preset-system-readonly');
    const systemEditable = document.getElementById('preset-system-editable');

    // Author's note elements
    const authorsNoteReadonly = document.getElementById('preset-authors-note-readonly');
    const authorsNoteEditable = document.getElementById('preset-authors-note-editable');

    presetName.textContent = preset.name;
    presetDescription.textContent = preset.description;
    presetInfo.style.display = 'block';

    // Check if built-in preset is modified
    const modifiedBadge = document.getElementById('preset-modified-badge');
    const restoreBtn = document.getElementById('restore-preset-btn');
    let isModified = false;

    if (isBuiltIn) {
      isModified = await invoke('is_builtin_preset_modified', { presetId: preset.id });
    }

    // Show/hide built-in badge and controls
    if (isBuiltIn) {
      builtInBadge.style.display = 'inline-block';
      modifiedBadge.style.display = isModified ? 'inline-block' : 'none';
      deleteBtn.style.display = 'none';
      duplicateBtn.style.display = 'inline-block';
      restoreBtn.style.display = isModified ? 'inline-block' : 'none';
      saveChangesBtn.style.display = 'inline-block';
      addInstructionBtn.style.display = 'inline-block';

      // Show editable versions (built-in presets are now editable)
      systemEditable.value = preset.system_additions || '';
      systemEditable.style.display = 'block';
      systemReadonly.style.display = 'none';

      authorsNoteEditable.value = preset.authors_note_default || '';
      authorsNoteEditable.style.display = 'block';
      authorsNoteReadonly.style.display = 'none';
    } else {
      builtInBadge.style.display = 'none';
      modifiedBadge.style.display = 'none';
      restoreBtn.style.display = 'none';
      deleteBtn.style.display = 'inline-block';
      duplicateBtn.style.display = 'none';
      saveChangesBtn.style.display = 'block';
      addInstructionBtn.style.display = 'inline-block';

      // Show editable versions
      systemEditable.value = preset.system_additions || '';
      systemEditable.style.display = 'block';
      systemReadonly.style.display = 'none';

      authorsNoteEditable.value = preset.authors_note_default || '';
      authorsNoteEditable.style.display = 'block';
      authorsNoteReadonly.style.display = 'none';
    }

    // Render instruction blocks (all presets are now editable)
    renderInstructionBlocks(preset.instructions, false);

    // Enable apply button
    applyBtn.disabled = false;
  } catch (error) {
    console.error('Failed to load preset details:', error);
    hidePresetInfo();
  }
}

// Apply selected preset
async function handleApplyPreset() {
  if (!currentCharacter) return;

  const presetSelect = document.getElementById('preset-select');
  const presetId = presetSelect.value || null;

  try {
    await invoke('set_active_preset', {
      characterId: currentCharacter.id,
      presetId
    });

    // Update local settings
    if (currentRoleplaySettings) {
      currentRoleplaySettings.active_preset_id = presetId;
    }

    // Update feature badges
    updateFeatureBadges();

    setStatus(presetId ? 'Preset applied' : 'Preset removed', 'success');
    setTimeout(() => setStatus('Ready'), 2000);
  } catch (error) {
    console.error('Failed to apply preset:', error);
    setStatus('Failed to apply preset', 'error');
    setTimeout(() => setStatus('Ready'), 2000);
  }
}

// Create custom preset
async function handleCreatePreset() {
  // Check if form already exists
  if (document.getElementById('preset-create-form')) return;

  const container = document.getElementById('presets-tab').querySelector('.roleplay-content');
  const createBtn = document.getElementById('create-preset-btn');

  // Create inline form
  const formDiv = document.createElement('div');
  formDiv.id = 'preset-create-form';
  formDiv.style.background = 'var(--bg-secondary)';
  formDiv.style.border = '2px solid var(--accent)';
  formDiv.style.borderRadius = '8px';
  formDiv.style.padding = '16px';
  formDiv.style.marginBottom = '16px';

  formDiv.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 12px;">
      <div style="font-weight: 600; color: var(--text-primary); margin-bottom: 4px;">Create Custom Preset</div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Name *</label>
        <input type="text" id="preset-create-name" placeholder="My Custom Preset" style="width: 100%;" />
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Description *</label>
        <textarea id="preset-create-desc" placeholder="What this preset does..." rows="3" style="width: 100%;"></textarea>
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">System Additions (optional)</label>
        <textarea id="preset-create-system" placeholder="Additional text to prepend to system prompt..." rows="3" style="width: 100%;"></textarea>
      </div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Default Author's Note (optional)</label>
        <textarea id="preset-create-note" placeholder="Default Author's Note if user hasn't set one..." rows="3" style="width: 100%;"></textarea>
      </div>
      <div style="display: flex; gap: 8px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn" id="preset-create-cancel">Cancel</button>
        <button type="button" class="worldinfo-btn" id="preset-create-save" style="background: var(--accent); color: white;">Create</button>
      </div>
    </div>
  `;

  container.insertBefore(formDiv, createBtn);
  document.getElementById('preset-create-name').focus();

  // Handle cancel
  document.getElementById('preset-create-cancel').addEventListener('click', () => {
    formDiv.remove();
  });

  // Handle save
  document.getElementById('preset-create-save').addEventListener('click', async () => {
    const name = document.getElementById('preset-create-name').value.trim();
    const description = document.getElementById('preset-create-desc').value.trim();
    const systemAdditions = document.getElementById('preset-create-system').value.trim();
    const authorsNoteDefault = document.getElementById('preset-create-note').value.trim();

    if (!name || !description) {
      alert('Name and description are required');
      return;
    }

    try {
      // Generate a simple ID from the name
      const id = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');

      const preset = {
        id: id,
        name,
        description,
        system_additions: systemAdditions || '',
        authors_note_default: authorsNoteDefault || '',
        instructions: [],
        format_hints: {
          wi_format: '[{content}]',
          scenario_format: '[Scenario: {content}]',
          personality_format: '[{char}\'s personality: {content}]'
        }
      };

      await invoke('save_custom_preset', { preset });

      formDiv.remove();
      setStatus('Custom preset created', 'success');
      setTimeout(() => setStatus('Ready'), 2000);

      // Reload presets
      await loadPresets();

      // Select the new preset
      document.getElementById('preset-select').value = id;
      await handlePresetSelect(id);
    } catch (error) {
      console.error('Failed to create preset:', error);
      alert(`Failed to create preset: ${error}`);
      setStatus('Failed to create preset', 'error');
      setTimeout(() => setStatus('Ready'), 2000);
    }
  });
}

// Render instruction blocks list
function renderInstructionBlocks(instructions, isReadOnly) {
  const listContainer = document.getElementById('preset-instructions-list');
  listContainer.innerHTML = '';

  if (!instructions || instructions.length === 0) {
    listContainer.innerHTML = '<div style="text-align: center; color: var(--text-secondary); font-size: 11px; padding: 12px;">No instruction blocks yet.</div>';
    return;
  }

  // Sort by order
  const sortedInstructions = [...instructions].sort((a, b) => a.order - b.order);

  sortedInstructions.forEach((instruction, index) => {
    const blockDiv = document.createElement('div');
    blockDiv.className = 'worldinfo-entry';
    blockDiv.style.marginBottom = '8px';
    blockDiv.style.padding = '8px';
    blockDiv.style.background = 'var(--bg-secondary)';
    blockDiv.style.borderRadius = '4px';
    blockDiv.style.cursor = 'pointer';
    blockDiv.style.transition = 'all 0.2s ease';
    blockDiv.dataset.instructionId = instruction.id;
    blockDiv.dataset.collapsed = 'false';

    // Enable drag and drop for non-readonly
    if (!isReadOnly) {
      blockDiv.draggable = true;
      blockDiv.style.cursor = 'move';

      // Drag event handlers
      blockDiv.addEventListener('dragstart', (e) => {
        e.dataTransfer.effectAllowed = 'move';
        e.dataTransfer.setData('text/plain', instruction.id);
        blockDiv.style.opacity = '0.5';
      });

      blockDiv.addEventListener('dragend', (e) => {
        blockDiv.style.opacity = '1';
        // Remove all drop indicators
        document.querySelectorAll('.worldinfo-entry').forEach(el => {
          el.style.borderTop = '';
          el.style.borderBottom = '';
        });
      });

      blockDiv.addEventListener('dragover', (e) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';

        // Show drop indicator
        const rect = blockDiv.getBoundingClientRect();
        const midpoint = rect.top + rect.height / 2;
        if (e.clientY < midpoint) {
          blockDiv.style.borderTop = '2px solid var(--accent)';
          blockDiv.style.borderBottom = '';
        } else {
          blockDiv.style.borderTop = '';
          blockDiv.style.borderBottom = '2px solid var(--accent)';
        }
      });

      blockDiv.addEventListener('dragleave', (e) => {
        blockDiv.style.borderTop = '';
        blockDiv.style.borderBottom = '';
      });

      blockDiv.addEventListener('drop', (e) => {
        e.preventDefault();
        blockDiv.style.borderTop = '';
        blockDiv.style.borderBottom = '';

        const draggedId = e.dataTransfer.getData('text/plain');
        const draggedInstruction = currentEditingPreset.instructions.find(i => i.id === draggedId);
        const dropInstruction = instruction;

        if (draggedId !== instruction.id && draggedInstruction) {
          // Determine drop position
          const rect = blockDiv.getBoundingClientRect();
          const midpoint = rect.top + rect.height / 2;
          const dropBefore = e.clientY < midpoint;

          // Reorder instructions
          const draggedOrder = draggedInstruction.order;
          const dropOrder = dropInstruction.order;

          if (dropBefore) {
            // Insert before
            if (draggedOrder < dropOrder) {
              // Moving down - shift items between draggedOrder and dropOrder-1 up
              currentEditingPreset.instructions.forEach(inst => {
                if (inst.order > draggedOrder && inst.order < dropOrder) {
                  inst.order--;
                }
              });
              draggedInstruction.order = dropOrder - 1;
            } else {
              // Moving up - shift items from dropOrder to draggedOrder-1 down
              currentEditingPreset.instructions.forEach(inst => {
                if (inst.order >= dropOrder && inst.order < draggedOrder) {
                  inst.order++;
                }
              });
              draggedInstruction.order = dropOrder;
            }
          } else {
            // Insert after
            if (draggedOrder < dropOrder) {
              // Moving down - shift items between draggedOrder+1 and dropOrder down
              currentEditingPreset.instructions.forEach(inst => {
                if (inst.order > draggedOrder && inst.order <= dropOrder) {
                  inst.order--;
                }
              });
              draggedInstruction.order = dropOrder;
            } else {
              // Moving up - shift items from dropOrder+1 to draggedOrder-1 down
              currentEditingPreset.instructions.forEach(inst => {
                if (inst.order > dropOrder && inst.order < draggedOrder) {
                  inst.order++;
                }
              });
              draggedInstruction.order = dropOrder + 1;
            }
          }

          // Re-render
          renderInstructionBlocks(currentEditingPreset.instructions, isReadOnly);
        }
      });
    }

    const header = document.createElement('div');
    header.style.display = 'flex';
    header.style.justifyContent = 'space-between';
    header.style.alignItems = 'center';
    header.style.marginBottom = '6px';
    header.style.userSelect = 'none';

    const leftSide = document.createElement('div');
    leftSide.style.display = 'flex';
    leftSide.style.alignItems = 'center';
    leftSide.style.gap = '8px';

    // Expand/collapse chevron
    const chevron = document.createElement('span');
    chevron.style.fontSize = '10px';
    chevron.style.transition = 'transform 0.2s ease';
    chevron.textContent = '▼';
    chevron.style.color = 'var(--text-secondary)';
    leftSide.appendChild(chevron);

    if (!isReadOnly) {
      // Drag handle
      const dragHandle = document.createElement('span');
      dragHandle.style.fontSize = '10px';
      dragHandle.style.color = 'var(--text-secondary)';
      dragHandle.textContent = '⋮⋮';
      dragHandle.style.cursor = 'move';
      leftSide.appendChild(dragHandle);

      // Checkbox for enabled/disabled
      const checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
      checkbox.checked = instruction.enabled;
      checkbox.addEventListener('change', (e) => {
        e.stopPropagation();
        instruction.enabled = checkbox.checked;
      });
      checkbox.addEventListener('click', (e) => e.stopPropagation());
      leftSide.appendChild(checkbox);
    }

    // Order badge
    const orderBadge = document.createElement('span');
    orderBadge.style.fontSize = '10px';
    orderBadge.style.color = 'var(--text-secondary)';
    orderBadge.style.background = 'var(--bg-primary)';
    orderBadge.style.padding = '2px 6px';
    orderBadge.style.borderRadius = '3px';
    orderBadge.textContent = `#${instruction.order}`;
    leftSide.appendChild(orderBadge);

    // Name
    const nameSpan = document.createElement('span');
    nameSpan.style.fontWeight = '500';
    nameSpan.style.fontSize = '11px';
    nameSpan.textContent = instruction.name;
    if (!instruction.enabled) {
      nameSpan.style.opacity = '0.5';
    }
    leftSide.appendChild(nameSpan);

    header.appendChild(leftSide);

    if (!isReadOnly) {
      // Control buttons
      const controls = document.createElement('div');
      controls.style.display = 'flex';
      controls.style.gap = '4px';

      // Edit button
      const editBtn = document.createElement('button');
      editBtn.className = 'worldinfo-btn';
      editBtn.textContent = 'Edit';
      editBtn.style.fontSize = '11px';
      editBtn.style.padding = '2px 6px';
      editBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        editInstruction(instruction);
      });
      controls.appendChild(editBtn);

      // Delete button
      const deleteBtn = document.createElement('button');
      deleteBtn.className = 'worldinfo-btn worldinfo-btn-danger';
      deleteBtn.textContent = 'Delete';
      deleteBtn.style.fontSize = '11px';
      deleteBtn.style.padding = '2px 6px';
      deleteBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        deleteInstruction(instruction.id);
      });
      controls.appendChild(deleteBtn);

      header.appendChild(controls);
    }

    // Content
    const contentDiv = document.createElement('div');
    contentDiv.className = 'instruction-content';
    contentDiv.style.fontSize = '11px';
    contentDiv.style.color = 'var(--text-secondary)';
    contentDiv.style.marginTop = '4px';
    contentDiv.style.whiteSpace = 'pre-wrap';
    contentDiv.style.overflow = 'hidden';
    contentDiv.style.transition = 'max-height 0.3s ease, opacity 0.3s ease';
    contentDiv.textContent = instruction.content;
    if (!instruction.enabled) {
      contentDiv.style.opacity = '0.5';
    }

    // Toggle expand/collapse on header click
    header.addEventListener('click', () => {
      const isCollapsed = blockDiv.dataset.collapsed === 'true';
      blockDiv.dataset.collapsed = isCollapsed ? 'false' : 'true';

      if (isCollapsed) {
        // Expand
        chevron.style.transform = 'rotate(0deg)';
        contentDiv.style.maxHeight = contentDiv.scrollHeight + 'px';
        contentDiv.style.opacity = '1';
        setTimeout(() => {
          contentDiv.style.maxHeight = 'none';
        }, 300);
      } else {
        // Collapse
        chevron.style.transform = 'rotate(-90deg)';
        contentDiv.style.maxHeight = contentDiv.scrollHeight + 'px';
        setTimeout(() => {
          contentDiv.style.maxHeight = '0';
          contentDiv.style.opacity = '0';
        }, 10);
      }
    });

    blockDiv.appendChild(header);
    blockDiv.appendChild(contentDiv);
    listContainer.appendChild(blockDiv);
  });
}

// Add new instruction block
function addInstructionBlock() {
  if (!currentEditingPreset) return;

  const listContainer = document.getElementById('preset-instructions-list');

  // Check if form already exists
  if (document.getElementById('instruction-add-form')) return;

  // Create inline form
  const formDiv = document.createElement('div');
  formDiv.id = 'instruction-add-form';
  formDiv.style.background = 'var(--bg-secondary)';
  formDiv.style.border = '2px solid var(--accent)';
  formDiv.style.borderRadius = '6px';
  formDiv.style.padding = '12px';
  formDiv.style.marginBottom = '8px';

  formDiv.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 10px;">
      <div style="font-weight: 500; font-size: 11px; color: var(--text-primary);">Add Instruction Block</div>
      <div>
        <label style="font-size: 11px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Name</label>
        <input type="text" id="inst-add-name" placeholder="Block name..." style="width: 100%; padding: 6px; font-size: 11px;" />
      </div>
      <div>
        <label style="font-size: 11px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Content</label>
        <textarea id="inst-add-content" placeholder="Instruction content..." rows="4" style="width: 100%; padding: 6px; font-size: 11px;"></textarea>
      </div>
      <div style="display: flex; gap: 6px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn" id="inst-add-cancel" style="font-size: 11px; padding: 4px 8px;">Cancel</button>
        <button type="button" class="worldinfo-btn" id="inst-add-save" style="background: var(--accent); color: white; font-size: 11px; padding: 4px 8px;">Add</button>
      </div>
    </div>
  `;

  listContainer.prepend(formDiv);
  document.getElementById('inst-add-name').focus();

  // Handle cancel
  document.getElementById('inst-add-cancel').addEventListener('click', () => {
    formDiv.remove();
  });

  // Handle save
  document.getElementById('inst-add-save').addEventListener('click', () => {
    const name = document.getElementById('inst-add-name').value.trim();
    const content = document.getElementById('inst-add-content').value.trim();

    if (!name || !content) {
      alert('Name and content are required');
      return;
    }

    // Generate ID and determine order
    const id = `inst_${Date.now()}`;
    const maxOrder = currentEditingPreset.instructions.length > 0
      ? Math.max(...currentEditingPreset.instructions.map(i => i.order))
      : 0;

    const newInstruction = {
      id,
      name,
      content,
      enabled: true,
      order: maxOrder + 1
    };

    currentEditingPreset.instructions.push(newInstruction);
    formDiv.remove();

    // Re-render
    renderInstructionBlocks(currentEditingPreset.instructions, false);
  });
}

// Edit instruction block
function editInstruction(instruction) {
  // Find the instruction block div
  const listContainer = document.getElementById('preset-instructions-list');
  const blocks = Array.from(listContainer.children);
  const blockDiv = blocks.find(el => {
    const header = el.querySelector('[style*="cursor: pointer"]');
    return header && header.textContent.includes(instruction.name);
  });

  if (!blockDiv) return;

  // Check if already editing
  if (blockDiv.querySelector('.instruction-edit-form')) return;

  // Hide original content
  const header = blockDiv.querySelector('[style*="cursor: pointer"]');
  const content = blockDiv.querySelector('.instruction-content');
  header.style.display = 'none';
  content.style.display = 'none';

  // Create edit form
  const editForm = document.createElement('div');
  editForm.className = 'instruction-edit-form';
  editForm.style.padding = '8px';
  editForm.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 10px;">
      <div style="font-weight: 500; font-size: 11px; color: var(--text-primary);">Edit Instruction Block</div>
      <div>
        <label style="font-size: 11px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Name</label>
        <input type="text" class="inst-edit-name" value="${instruction.name}" style="width: 100%; padding: 6px; font-size: 11px;" />
      </div>
      <div>
        <label style="font-size: 11px; color: var(--text-secondary); display: block; margin-bottom: 4px;">Content</label>
        <textarea class="inst-edit-content" rows="4" style="width: 100%; padding: 6px; font-size: 11px;">${instruction.content}</textarea>
      </div>
      <div style="display: flex; gap: 6px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn inst-edit-cancel" style="font-size: 11px; padding: 4px 8px;">Cancel</button>
        <button type="button" class="worldinfo-btn inst-edit-save" style="background: var(--accent); color: white; font-size: 11px; padding: 4px 8px;">Save</button>
      </div>
    </div>
  `;

  blockDiv.appendChild(editForm);
  editForm.querySelector('.inst-edit-name').focus();

  // Handle cancel
  editForm.querySelector('.inst-edit-cancel').addEventListener('click', () => {
    header.style.display = '';
    content.style.display = '';
    editForm.remove();
  });

  // Handle save
  editForm.querySelector('.inst-edit-save').addEventListener('click', () => {
    const newName = editForm.querySelector('.inst-edit-name').value.trim();
    const newContent = editForm.querySelector('.inst-edit-content').value.trim();

    if (!newName || !newContent) {
      alert('Name and content are required');
      return;
    }

    instruction.name = newName;
    instruction.content = newContent;

    // Re-render
    renderInstructionBlocks(currentEditingPreset.instructions, false);
  });
}

// Delete instruction block
function deleteInstruction(instructionId) {
  if (!confirm('Delete this instruction block?')) return;

  if (!currentEditingPreset) return;

  currentEditingPreset.instructions = currentEditingPreset.instructions.filter(
    i => i.id !== instructionId
  );

  // Re-render
  renderInstructionBlocks(currentEditingPreset.instructions, false);
}

// Move instruction block up or down
function moveInstruction(instructionId, direction) {
  if (!currentEditingPreset) return;

  const instructions = currentEditingPreset.instructions.sort((a, b) => a.order - b.order);
  const index = instructions.findIndex(i => i.id === instructionId);

  if (index === -1) return;
  if (direction === -1 && index === 0) return; // Already at top
  if (direction === 1 && index === instructions.length - 1) return; // Already at bottom

  const targetIndex = index + direction;

  // Swap orders
  const temp = instructions[index].order;
  instructions[index].order = instructions[targetIndex].order;
  instructions[targetIndex].order = temp;

  // Re-render
  renderInstructionBlocks(currentEditingPreset.instructions, false);
}

// Save preset changes
async function savePresetChanges() {
  if (!currentEditingPreset) return;

  try {
    // Update system additions and author's note from UI
    const systemEditable = document.getElementById('preset-system-editable');
    const authorsNoteEditable = document.getElementById('preset-authors-note-editable');

    currentEditingPreset.system_additions = systemEditable.value;
    currentEditingPreset.authors_note_default = authorsNoteEditable.value;

    // Save via update_preset_instructions command
    await invoke('update_preset_instructions', {
      presetId: currentEditingPreset.id,
      instructions: currentEditingPreset.instructions
    });

    // Also save the full preset to update system_additions and authors_note_default
    await invoke('save_custom_preset', { preset: currentEditingPreset });

    setStatus('Preset saved', 'success');
    setTimeout(() => setStatus('Ready'), 2000);

    // Reload to show updated preset
    await handlePresetSelect(currentEditingPreset.id);
  } catch (error) {
    console.error('Failed to save preset changes:', error);
    alert(`Failed to save changes: ${error}`);
    setStatus('Failed to save preset', 'error');
    setTimeout(() => setStatus('Ready'), 2000);
  }
}

// Delete custom preset
async function deletePreset() {
  if (!currentEditingPreset) return;

  if (!confirm(`Delete preset "${currentEditingPreset.name}"? This cannot be undone.`)) return;

  try {
    await invoke('delete_custom_preset', { presetId: currentEditingPreset.id });

    setStatus('Preset deleted', 'success');
    setTimeout(() => setStatus('Ready'), 2000);

    // Clear selection and reload presets
    document.getElementById('preset-select').value = '';
    currentEditingPreset = null;
    hidePresetInfo();
    await loadPresets();
  } catch (error) {
    console.error('Failed to delete preset:', error);
    alert(`Failed to delete preset: ${error}`);
    setStatus('Failed to delete preset', 'error');
    setTimeout(() => setStatus('Ready'), 2000);
  }
}

// Duplicate preset (create editable copy)
async function duplicatePreset() {
  if (!currentEditingPreset) return;

  // Check if form already exists
  if (document.getElementById('preset-duplicate-form')) return;

  const presetInfo = document.getElementById('preset-info');
  const formDiv = document.createElement('div');
  formDiv.id = 'preset-duplicate-form';
  formDiv.style.background = 'var(--bg-secondary)';
  formDiv.style.border = '2px solid var(--accent)';
  formDiv.style.borderRadius = '8px';
  formDiv.style.padding = '16px';
  formDiv.style.marginBottom = '16px';

  formDiv.innerHTML = `
    <div style="display: flex; flex-direction: column; gap: 12px;">
      <div style="font-weight: 600; color: var(--text-primary); margin-bottom: 4px;">Duplicate Preset</div>
      <div>
        <label style="font-size: 12px; color: var(--text-secondary); display: block; margin-bottom: 4px;">New Preset Name *</label>
        <input type="text" id="preset-duplicate-name" placeholder="My Preset (Copy)" value="${currentEditingPreset.name} (Copy)" style="width: 100%;" />
      </div>
      <div style="display: flex; gap: 8px; justify-content: flex-end;">
        <button type="button" class="worldinfo-btn" id="preset-duplicate-cancel">Cancel</button>
        <button type="button" class="worldinfo-btn" id="preset-duplicate-save" style="background: var(--accent); color: white;">Duplicate</button>
      </div>
    </div>
  `;

  presetInfo.parentNode.insertBefore(formDiv, presetInfo.nextSibling);
  document.getElementById('preset-duplicate-name').focus();
  document.getElementById('preset-duplicate-name').select();

  // Cancel button
  document.getElementById('preset-duplicate-cancel').addEventListener('click', () => {
    formDiv.remove();
  });

  // Duplicate button
  document.getElementById('preset-duplicate-save').addEventListener('click', async () => {
    const newName = document.getElementById('preset-duplicate-name').value.trim();
    if (!newName) {
      alert('Please enter a preset name');
      return;
    }

    try {
      const duplicatedPreset = await invoke('duplicate_preset', {
        sourcePresetId: currentEditingPreset.id,
        newName: newName
      });

      setStatus('Preset duplicated successfully', 'success');
      setTimeout(() => setStatus('Ready'), 2000);

      // Remove form
      formDiv.remove();

      // Reload presets
      await loadPresets();

      // Select the new preset
      document.getElementById('preset-select').value = duplicatedPreset.id;
      await handlePresetSelect(duplicatedPreset.id);
    } catch (error) {
      console.error('Failed to duplicate preset:', error);
      alert(`Failed to duplicate preset: ${error}`);
      setStatus('Failed to duplicate preset', 'error');
      setTimeout(() => setStatus('Ready'), 2000);
    }
  });
}

// Restore built-in preset to default
async function restoreBuiltinPreset() {
  if (!currentEditingPreset) return;

  const builtInIds = ['default', 'roleplay', 'creative-writing', 'assistant'];
  if (!builtInIds.includes(currentEditingPreset.id)) {
    alert('Can only restore built-in presets');
    return;
  }

  if (!confirm(`Are you sure you want to restore "${currentEditingPreset.name}" to its default settings? All your modifications will be lost.`)) {
    return;
  }

  try {
    const restoredPreset = await invoke('restore_builtin_preset', {
      presetId: currentEditingPreset.id
    });

    setStatus('Preset restored to default successfully', 'success');
    setTimeout(() => setStatus('Ready'), 2000);

    // Reload presets
    await loadPresets();

    // Re-select the restored preset to refresh the UI
    await handlePresetSelect(restoredPreset.id);
  } catch (error) {
    console.error('Failed to restore preset:', error);
    alert(`Failed to restore preset: ${error}`);
    setStatus('Failed to restore preset', 'error');
    setTimeout(() => setStatus('Ready'), 2000);
  }
}

// Load existing config if available
async function loadExistingConfig() {
  console.log('Loading existing config...');
  try {
    const config = await invoke('get_api_config');
    console.log('Loaded config:', config);
    document.getElementById('api-base-url').value = config.base_url;
    document.getElementById('api-key').value = config.api_key;
    document.getElementById('stream-toggle').checked = config.stream || false;
    document.getElementById('context-limit').value = config.context_limit || 200000;

    const modelSelect = document.getElementById('model-select');
    modelSelect.innerHTML = ''; // Clear existing options
    const option = document.createElement('option');
    option.value = config.model;
    option.textContent = config.model;
    option.selected = true;
    modelSelect.appendChild(option);

    // Show the model group since we have a saved model
    document.getElementById('models-group').style.display = 'flex';
    document.getElementById('save-settings-btn').disabled = false;

    // Load characters
    await loadCharacters();
  } catch (error) {
    console.error('Failed to load existing config:', error);
    addMessage('API not configured. Please configure your API settings.', false);
    showSettings();
  }
}

// Initialize app
window.addEventListener('DOMContentLoaded', () => {
  messageInput = document.getElementById('message-input');
  messagesContainer = document.getElementById('messages');
  chatForm = document.getElementById('chat-form');
  sendBtn = document.getElementById('send-btn');
  statusText = document.getElementById('status-text');
  settingsPanel = document.getElementById('settings-panel');
  chatView = document.getElementById('chat-view');
  characterSelect = document.getElementById('character-select');
  characterHeaderName = document.getElementById('character-header-name');
  newCharacterBtn = document.getElementById('new-character-btn');

  chatForm.addEventListener('submit', handleSubmit);
  document.getElementById('settings-form').addEventListener('submit', handleSaveSettings);
  document.getElementById('character-form').addEventListener('submit', handleSaveCharacter);
  document.getElementById('validate-btn').addEventListener('click', handleValidate);

  setupAppControls();
  setupKeyboardShortcuts();
  setupTabs();

  // Avatar modal close handlers
  const avatarModal = document.getElementById('avatar-modal');
  const avatarModalOverlay = document.querySelector('.avatar-modal-overlay');

  avatarModalOverlay.addEventListener('click', hideAvatarModal);

  // ESC key to close modal
  // Global keyboard shortcuts
  document.addEventListener('keydown', (e) => {
    // Escape key handling
    if (e.key === 'Escape') {
      // Close new character modal
      const newCharacterModal = document.getElementById('new-character-modal');
      if (newCharacterModal && newCharacterModal.style.display !== 'none') {
        document.getElementById('cancel-new-character-btn').click();
        return;
      }

      // Close avatar modal
      if (avatarModal.style.display !== 'none') {
        hideAvatarModal();
        return;
      }

      // Close roleplay panel
      const roleplayPanel = document.getElementById('roleplay-panel');
      if (roleplayPanel && roleplayPanel.classList.contains('active')) {
        document.getElementById('close-roleplay-btn').click();
        return;
      }

      // Close settings panel
      const settingsPanel = document.getElementById('settings-panel');
      if (settingsPanel && settingsPanel.classList.contains('active')) {
        document.getElementById('close-settings-btn').click();
        return;
      }

      // Cancel message editing
      const editActions = document.querySelector('.message-edit-actions');
      if (editActions) {
        const cancelBtn = editActions.querySelector('.message-edit-cancel');
        if (cancelBtn) cancelBtn.click();
        return;
      }
    }

    // Up Arrow - Edit last user message (when input is focused and empty/at start)
    if (e.key === 'ArrowUp' && e.target === messageInput && messageInput.selectionStart === 0) {
      const messages = document.querySelectorAll('.message.user');
      if (messages.length > 0) {
        const lastUserMessage = messages[messages.length - 1];
        const editBtn = lastUserMessage.querySelector('.message-action-btn[title="Edit message"]');
        if (editBtn) {
          e.preventDefault();
          editBtn.click();
        }
      }
      return;
    }

    // Left Arrow - Previous swipe (when not in input)
    if (e.key === 'ArrowLeft' && e.target !== messageInput && !e.target.matches('input, textarea, select')) {
      const lastAssistantMessage = [...document.querySelectorAll('.message.assistant')].pop();
      if (lastAssistantMessage) {
        const prevBtn = lastAssistantMessage.querySelector('.swipe-prev');
        if (prevBtn && !prevBtn.disabled) {
          e.preventDefault();
          prevBtn.click();
        }
      }
      return;
    }

    // Right Arrow - Next swipe (when not in input)
    if (e.key === 'ArrowRight' && e.target !== messageInput && !e.target.matches('input, textarea, select')) {
      const lastAssistantMessage = [...document.querySelectorAll('.message.assistant')].pop();
      if (lastAssistantMessage) {
        const nextBtn = lastAssistantMessage.querySelector('.swipe-next');
        if (nextBtn && !nextBtn.disabled) {
          e.preventDefault();
          nextBtn.click();
        }
      }
      return;
    }

    // Ctrl/Cmd + K - Focus message input
    if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
      e.preventDefault();
      messageInput.focus();
      return;
    }

    // Ctrl/Cmd + / - Toggle roleplay panel
    if ((e.ctrlKey || e.metaKey) && e.key === '/') {
      e.preventDefault();
      document.getElementById('roleplay-btn').click();
      return;
    }

    // Ctrl/Cmd + Enter - Send message (alternative to Enter)
    if ((e.ctrlKey || e.metaKey) && e.key === 'Enter' && e.target === messageInput) {
      e.preventDefault();
      handleSubmit(e);
      return;
    }
  });

  messageInput.focus();
  setStatus('Ready');

  // Load saved preferences before anything else
  loadSavedTheme();
  loadSavedViewMode();
  loadSavedFontSize();

  loadExistingConfig();
});
