WTR LAB Novel Image Generator

Enhance web novel reading with this AI-powered script: Generate stunning images from text selections via Gemini-enhanced prompts in 100+ artistic styles. Multi-provider support (Pollinations, AI Horde, Google Imagen, OpenAI APIs). Includes modern UI, debug console w/ logs, history management, config import/export, cache handling, and experience optimizations. Transform text into visuals effortlessly!.

// ==UserScript==
// @name         WTR LAB Novel Image Generator
// @namespace    http://tampermonkey.net/
// @version      5.7
// @description  Enhance web novel reading with this AI-powered script: Generate stunning images from text selections via Gemini-enhanced prompts in 100+ artistic styles. Multi-provider support (Pollinations, AI Horde, Google Imagen, OpenAI APIs). Includes modern UI, debug console w/ logs, history management, config import/export, cache handling, and experience optimizations. Transform text into visuals effortlessly!.
// @author       MasuRii
// @match        https://wtr-lab.com/en/novel/*/*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=wtr-lab.com
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @connect      *
// @license      MIT
// ==/UserScript==

;(function () {
	'use strict'

	// --- 1. STYLES ---
	const styles = `
	       /* === CSS Custom Properties (Design Tokens) === */
	       :root {
	           /* Color Palette - Modern Dark Theme */
	           --nig-color-bg-primary: #1a1a1e;
	           --nig-color-bg-secondary: #2d2d32;
	           --nig-color-bg-tertiary: #3a3a40;
	           --nig-color-bg-elevated: #404046;
	           --nig-color-text-primary: #f0f0f0;
	           --nig-color-text-secondary: #b4b4b8;
	           --nig-color-text-muted: #8a8a8e;
	           --nig-color-border: #55555a;
	           --nig-color-border-light: #6a6a6e;
	           
	           /* Accent Colors */
	           --nig-color-accent-primary: #6366f1;
	           --nig-color-accent-secondary: #8b5cf6;
	           --nig-color-accent-success: #10b981;
	           --nig-color-accent-warning: #f59e0b;
	           --nig-color-accent-error: #ef4444;
	           
	           /* Interactive States */
	           --nig-color-hover-primary: #5855eb;
	           --nig-color-hover-secondary: #7c3aed;
	           --nig-color-hover-success: #059669;
	           --nig-color-hover-error: #dc2626;
	           
	           /* Spacing Scale */
	           --nig-space-xs: 0.25rem;
	           --nig-space-sm: 0.5rem;
	           --nig-space-md: 0.75rem;
	           --nig-space-lg: 1rem;
	           --nig-space-xl: 1.5rem;
	           --nig-space-2xl: 2rem;
	           --nig-space-3xl: 3rem;
	           
	           /* Typography Scale */
	           --nig-font-size-xs: 0.75rem;
	           --nig-font-size-sm: 0.875rem;
	           --nig-font-size-base: 1rem;
	           --nig-font-size-lg: 1.125rem;
	           --nig-font-size-xl: 1.25rem;
	           --nig-font-size-2xl: 1.5rem;
	           --nig-font-size-3xl: 1.875rem;
	           
	           /* Border Radius */
	           --nig-radius-sm: 0.375rem;
	           --nig-radius-md: 0.5rem;
	           --nig-radius-lg: 0.75rem;
	           --nig-radius-xl: 1rem;
	           
	           /* Shadows */
	           --nig-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
	           --nig-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
	           --nig-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -2px rgba(0, 0, 0, 0.4);
	           --nig-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.6), 0 10px 10px -5px rgba(0, 0, 0, 0.5);
	           
	           /* Transitions */
	           --nig-transition-fast: 0.15s ease-out;
	           --nig-transition-normal: 0.2s ease-out;
	           --nig-transition-slow: 0.3s ease-out;
	           
	           /* Breakpoints */
	           --nig-breakpoint-sm: 640px;
	           --nig-breakpoint-md: 768px;
	           --nig-breakpoint-lg: 1024px;
	           --nig-breakpoint-xl: 1280px;
	       }

	       /* === Base Components === */
	       .nig-button {
	           position: absolute;
	           z-index: 99998;
	           background: var(--nig-color-accent-primary);
	           color: white;
	           border: none;
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-sm) var(--nig-space-md);
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           cursor: pointer;
	           box-shadow: var(--nig-shadow-md);
	           display: none;
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	           transition: all var(--nig-transition-normal);
	           transform: translateY(0);
	       }
	       .nig-button:hover {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-lg);
	       }
	       .nig-button:active {
	           transform: translateY(0);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-modal-overlay {
	           position: fixed;
	           top: 0;
	           left: 0;
	           width: 100%;
	           height: 100%;
	           background: rgba(0, 0, 0, 0.7);
	           backdrop-filter: blur(8px);
	           z-index: 99999;
	           display: flex;
	           flex-direction: column;
	           justify-content: center;
	           align-items: center;
	           padding: var(--nig-space-lg);
	       }

	       .nig-modal-content {
	           background: var(--nig-color-bg-secondary);
	           color: var(--nig-color-text-primary);
	           padding: var(--nig-space-2xl);
	           border-radius: var(--nig-radius-xl);
	           box-shadow: var(--nig-shadow-xl);
	           width: 100%;
	           max-width: 900px;
	           max-height: 90vh;
	           overflow-y: auto;
	           position: relative;
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	           border: 1px solid var(--nig-color-border);
	           animation: nig-modal-appear 0.2s ease-out;
	       }

	       @keyframes nig-modal-appear {
	           from {
	               opacity: 0;
	               transform: scale(0.95) translateY(-10px);
	           }
	           to {
	               opacity: 1;
	               transform: scale(1) translateY(0);
	           }
	       }

	       .nig-modal-content ul {
	           padding-left: var(--nig-space-xl);
	       }

	       .nig-modal-content li {
	           margin-bottom: var(--nig-space-md);
	       }

	       .nig-close-btn {
	           position: absolute;
	           top: var(--nig-space-lg);
	           right: var(--nig-space-lg);
	           font-size: var(--nig-font-size-2xl);
	           font-weight: 300;
	           cursor: pointer;
	           color: var(--nig-color-text-muted);
	           width: 32px;
	           height: 32px;
	           display: flex;
	           align-items: center;
	           justify-content: center;
	           border-radius: var(--nig-radius-md);
	           transition: all var(--nig-transition-fast);
	       }

	       .nig-close-btn:hover {
	           color: var(--nig-color-text-primary);
	           background: var(--nig-color-bg-tertiary);
	       }

	       .nig-modal-content h2 {
	           margin-top: 0;
	           border-bottom: 1px solid var(--nig-color-border);
	           padding-bottom: var(--nig-space-lg);
	           font-size: var(--nig-font-size-2xl);
	           font-weight: 600;
	           letter-spacing: -0.025em;
	       }

	       /* === Form Elements === */
	       .nig-form-group {
	           margin-bottom: var(--nig-space-xl);
	       }

	       .nig-form-group label {
	           display: block;
	           margin-bottom: var(--nig-space-sm);
	           font-weight: 500;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-sm);
	       }

	       .nig-form-group small.nig-hint {
	           color: var(--nig-color-text-muted);
	           font-weight: normal;
	           display: block;
	           margin-top: var(--nig-space-sm);
	           margin-bottom: var(--nig-space-sm);
	           min-height: 1.2em;
	           font-size: var(--nig-font-size-xs);
	           line-height: 1.4;
	       }

	       .nig-form-group input,
	       .nig-form-group select,
	       .nig-form-group textarea {
	           width: 100%;
	           padding: var(--nig-space-sm) var(--nig-space-md);
	           border-radius: var(--nig-radius-md);
	           border: 1px solid var(--nig-color-border);
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-sm);
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	           transition: all var(--nig-transition-fast);
	           outline: none;
	       }

	       .nig-form-group input:focus,
	       .nig-form-group select:focus,
	       .nig-form-group textarea:focus {
	           border-color: var(--nig-color-accent-primary);
	           box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
	           background: var(--nig-color-bg-elevated);
	       }

	       .nig-form-group select:disabled {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-muted);
	           cursor: not-allowed;
	       }

	       .nig-form-group textarea {
	           resize: vertical;
	           min-height: 80px;
	           line-height: 1.5;
	       }

	       .nig-form-group-inline {
	           display: grid;
	           grid-template-columns: 1fr 1fr;
	           gap: var(--nig-space-lg);
	           align-items: end;
	       }

	       .nig-form-group-inline label {
	           margin-bottom: var(--nig-space-sm);
	       }

	       .nig-checkbox-group {
	           display: flex;
	           flex-wrap: wrap;
	           gap: var(--nig-space-lg);
	       }

	       .nig-checkbox-group label {
	           display: flex;
	           align-items: center;
	           margin-right: 0;
	           font-weight: normal;
	           cursor: pointer;
	           color: var(--nig-color-text-secondary);
	       }

	       .nig-checkbox-group input {
	           width: auto;
	           margin-right: var(--nig-space-sm);
	           margin-bottom: 0;
	           transform: scale(1.1);
	       }

	       /* === Buttons === */
	       .nig-save-btn {
	           background: var(--nig-color-accent-success);
	           color: white;
	           padding: var(--nig-space-md) var(--nig-space-xl);
	           border: none;
	           border-radius: var(--nig-radius-md);
	           cursor: pointer;
	           font-size: var(--nig-font-size-base);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	           box-shadow: var(--nig-shadow-sm);
	           display: inline-flex;
	           align-items: center;
	           justify-content: center;
	           gap: var(--nig-space-sm);
	       }

	       .nig-save-btn:hover {
	           background: var(--nig-color-hover-success);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-md);
	       }

	       .nig-fetch-models-btn {
	           padding: var(--nig-space-sm) var(--nig-space-md);
	           margin-left: var(--nig-space-md);
	           border-radius: var(--nig-radius-md);
	           border: 1px solid var(--nig-color-border);
	           background: var(--nig-color-accent-primary);
	           color: white;
	           cursor: pointer;
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-fetch-models-btn:hover {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	       }

	       .nig-delete-btn {
	           padding: var(--nig-space-sm) var(--nig-space-md);
	           margin-left: var(--nig-space-md);
	           border-radius: var(--nig-radius-md);
	           border: 1px solid var(--nig-color-border);
	           background: var(--nig-color-accent-error);
	           color: white;
	           cursor: pointer;
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-delete-btn:hover {
	           background: var(--nig-color-hover-error);
	           transform: translateY(-1px);
	       }

	       .nig-history-cleanup-btn {
	           background: var(--nig-color-accent-error);
	           color: white;
	           padding: var(--nig-space-sm) var(--nig-space-md);
	           border: none;
	           border-radius: var(--nig-radius-md);
	           cursor: pointer;
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	           display: inline-flex;
	           align-items: center;
	           justify-content: center;
	           gap: var(--nig-space-sm);
	       }

	       .nig-history-cleanup-btn:hover {
	           background: var(--nig-color-hover-error);
	           transform: translateY(-1px);
	       }

	       /* === Tab System === */
	       .nig-tabs {
	           display: flex;
	           border-bottom: 1px solid var(--nig-color-border);
	           margin-bottom: var(--nig-space-xl);
	           overflow-x: auto;
	           scrollbar-width: none;
	           -ms-overflow-style: none;
	       }

	       .nig-tabs::-webkit-scrollbar {
	           display: none;
	       }

	       .nig-tab {
	           padding: var(--nig-space-md) var(--nig-space-xl);
	           cursor: pointer;
	           border-radius: var(--nig-radius-md) var(--nig-radius-md) 0 0;
	           background: transparent;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           transition: all var(--nig-transition-fast);
	           white-space: nowrap;
	           border: 1px solid transparent;
	           border-bottom: none;
	       }

	       .nig-tab:hover {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-primary);
	       }

	       .nig-tab.active {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-primary);
	           border: 1px solid var(--nig-color-border);
	           border-bottom: 1px solid var(--nig-color-bg-tertiary);
	           box-shadow: 0 -2px 0 var(--nig-color-accent-primary) inset;
	       }

	       .nig-tab-content {
	           display: none;
	           animation: nig-content-fade 0.2s ease-out;
	       }

	       .nig-tab-content.active {
	           display: block;
	       }

	       @keyframes nig-content-fade {
	           from {
	               opacity: 0;
	               transform: translateY(10px);
	           }
	           to {
	               opacity: 1;
	               transform: translateY(0);
	           }
	       }

	       /* === Modern Utilities Tab === */
	       .nig-utilities-grid {
	           display: grid;
	           grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	           gap: var(--nig-space-xl);
	           margin-top: var(--nig-space-xl);
	       }

	       .nig-utility-card {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-xl);
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-utility-card:hover {
	           border-color: var(--nig-color-border-light);
	           box-shadow: var(--nig-shadow-md);
	           transform: translateY(-2px);
	       }

	       .nig-utility-card h4 {
	           margin: 0 0 var(--nig-space-md) 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-lg);
	           font-weight: 600;
	       }

	       .nig-utility-card p {
	           margin: 0 0 var(--nig-space-lg) 0;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           line-height: 1.5;
	       }

	       .nig-utility-card .nig-save-btn {
	           width: 100%;
	           justify-content: center;
	       }

	       /* === Enhanced Utility Card Structure === */
	       .nig-card-header {
	           display: flex;
	           align-items: flex-start;
	           gap: var(--nig-space-md);
	           margin-bottom: var(--nig-space-lg);
	       }


	       .nig-card-title {
	           flex: 1;
	           min-width: 0;
	       }

	       .nig-card-title h4 {
	           margin: 0 0 var(--nig-space-sm) 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-lg);
	           font-weight: 600;
	           line-height: 1.3;
	       }

	       .nig-card-title p {
	           margin: 0;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           line-height: 1.5;
	       }

	       .nig-card-actions {
	           margin-bottom: var(--nig-space-lg);
	       }

	       .nig-card-secondary-actions {
	           display: grid;
	           grid-template-columns: 1fr 1fr;
	           gap: var(--nig-space-sm);
	           margin-bottom: var(--nig-space-lg);
	       }

	       .nig-card-footer {
	           padding-top: var(--nig-space-md);
	            border-top: 1px solid var(--nig-color-border);
	           margin-top: auto;
	       }

	       /* === Modern Button Variants === */
	       .nig-btn-primary {
	           width: 100%;
	            background: var(--nig-color-accent-warning);
	           color: white;
	           border: none;
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-md) var(--nig-space-lg);
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           cursor: pointer;
	           display: flex;
	           align-items: center;
	           justify-content: center;
	           gap: var(--nig-space-sm);
	           transition: all var(--nig-transition-normal);
	           box-shadow: var(--nig-shadow-sm);
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	       }

	       .nig-btn-primary:hover {
	           background: var(--nig-color-hover-error);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-md);
	       }

	       .nig-btn-primary:active {
	           transform: translateY(0);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-btn-secondary {
	           background: var(--nig-color-accent-primary);
	           color: white;
	           border: none;
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-xs) var(--nig-space-sm);
	           font-size: var(--nig-font-size-xs);
	           font-weight: 500;
	           cursor: pointer;
	           display: flex;
	           align-items: center;
	           justify-content: center;
	           gap: var(--nig-space-xs);
	           transition: all var(--nig-transition-normal);
	           box-shadow: var(--nig-shadow-sm);
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	           min-height: 36px;
	       }

	       .nig-btn-secondary:hover {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-md);
	       }

	       .nig-btn-secondary:active {
	           transform: translateY(0);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-btn-secondary.nig-btn-error {
	           background: var(--nig-color-accent-error);
	           color: white;
	       }

	       .nig-btn-secondary.nig-btn-error:hover {
	           background: var(--nig-color-hover-error);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-md);
	       }

	       .nig-btn-secondary .material-symbols-outlined {
	           font-size: var(--nig-font-size-xs);
	       }

	       .nig-btn-primary .material-symbols-outlined {
	           font-size: var(--nig-font-size-sm);
	       }

	       /* === History System === */
	       .nig-history-list {
	           list-style: none;
	           padding: 0;
	           max-height: 400px;
	           overflow-y: auto;
	       }

	       .nig-history-item {
	           background: var(--nig-color-bg-tertiary);
	           padding: var(--nig-space-lg);
	           border-radius: var(--nig-radius-md);
	           margin-bottom: var(--nig-space-md);
	           border: 1px solid var(--nig-color-border);
	           transition: all var(--nig-transition-fast);
	       }

	       .nig-history-item:hover {
	           border-color: var(--nig-color-border-light);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-history-item small {
	           display: block;
	           color: var(--nig-color-text-muted);
	           margin-bottom: var(--nig-space-sm);
	           font-size: var(--nig-font-size-xs);
	       }

	       .nig-history-item a {
	           color: var(--nig-color-accent-primary);
	           text-decoration: none;
	           word-break: break-all;
	           font-weight: 500;
	           transition: color var(--nig-transition-fast);
	       }

	       .nig-history-item a:hover {
	           color: var(--nig-color-hover-primary);
	       }

	       .nig-history-cleanup {
	           padding: var(--nig-space-lg);
	           background: var(--nig-color-bg-tertiary);
	           border-radius: var(--nig-radius-md);
	           margin-bottom: var(--nig-space-xl);
	           display: flex;
	           align-items: center;
	           gap: var(--nig-space-lg);
	           border: 1px solid var(--nig-color-border);
	       }

	       .nig-history-cleanup input[type="number"] {
	           width: 80px;
	           padding: var(--nig-space-sm);
	       }

	       /* === Image Gallery === */
	       .nig-image-gallery {
	           display: grid;
	           grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	           gap: var(--nig-space-xl);
	           margin-top: var(--nig-space-xl);
	       }

	       .nig-image-container {
	           position: relative;
	           border-radius: var(--nig-radius-lg);
	           overflow: hidden;
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-image-container:hover {
	           box-shadow: var(--nig-shadow-lg);
	           transform: translateY(-2px);
	       }

	       .nig-image-container img {
	           width: 100%;
	           height: auto;
	           display: block;
	           transition: transform var(--nig-transition-slow);
	       }

	       .nig-image-container:hover img {
	           transform: scale(1.02);
	       }

	       .nig-image-actions {
	           position: absolute;
	           top: var(--nig-space-md);
	           right: var(--nig-space-md);
	           display: flex;
	           gap: var(--nig-space-sm);
	           background: rgba(0, 0, 0, 0.7);
	           backdrop-filter: blur(10px);
	           padding: var(--nig-space-sm);
	           border-radius: var(--nig-radius-md);
	           opacity: 0;
	           transition: opacity var(--nig-transition-normal);
	       }

	       .nig-image-container:hover .nig-image-actions {
	           opacity: 1;
	       }

	       .nig-image-actions button {
	           background: rgba(0, 0, 0, 0.8);
	           border: none;
	           border-radius: var(--nig-radius-md);
	           width: 36px;
	           height: 36px;
	           cursor: pointer;
	           display: flex;
	           align-items: center;
	           justify-content: center;
	           font-size: var(--nig-font-size-base);
	           color: white;
	           transition: all var(--nig-transition-fast);
	       }

	       .nig-image-actions button:hover {
	           background: white;
	           transform: scale(1.1);
	       }

	       .material-symbols-outlined {
	           font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
	           font-size: 18px;
	       }

	       .nig-api-prompt-link {
	           color: var(--nig-color-accent-primary);
	           text-decoration: none;
	           transition: color var(--nig-transition-fast);
	       }

	       .nig-api-prompt-link:hover {
	           color: var(--nig-color-hover-primary);
	           text-decoration: underline;
	       }

	       .nig-provider-settings {
	           display: none;
	       }

	       /* === Prompt Container === */
	       .nig-prompt-container {
	           background: var(--nig-color-bg-tertiary);
	           border-radius: var(--nig-radius-md);
	           margin-bottom: var(--nig-space-lg);
	           border: 1px solid var(--nig-color-border);
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-prompt-header {
	           padding: var(--nig-space-lg);
	           cursor: pointer;
	           font-weight: 500;
	           display: flex;
	           align-items: center;
	           user-select: none;
	           color: var(--nig-color-text-primary);
	           transition: color var(--nig-transition-fast);
	       }

	       .nig-prompt-header:hover {
	           color: var(--nig-color-accent-primary);
	       }

	       .nig-prompt-header::before {
	           content: '▸';
	           margin-right: var(--nig-space-md);
	           transition: transform var(--nig-transition-normal);
	           color: var(--nig-color-text-muted);
	       }

	       .nig-prompt-container.expanded .nig-prompt-header::before {
	           transform: rotate(90deg);
	           color: var(--nig-color-accent-primary);
	       }

	       .nig-prompt-text {
	           display: none;
	           padding: 0 var(--nig-space-lg) var(--nig-space-lg) var(--nig-space-lg);
	           border-top: 1px solid var(--nig-color-border);
	           word-break: break-word;
	           color: var(--nig-color-text-secondary);
	           line-height: 1.6;
	       }

	       .nig-prompt-container.expanded .nig-prompt-text {
	           display: block;
	           animation: nig-expand 0.2s ease-out;
	       }

	       @keyframes nig-expand {
	           from {
	               opacity: 0;
	               max-height: 0;
	           }
	           to {
	               opacity: 1;
	               max-height: 200px;
	           }
	       }

	       /* === Button Footer === */
	       .nig-button-footer {
	           margin-top: var(--nig-space-3xl);
	           padding-top: var(--nig-space-xl);
	           border-top: 1px solid var(--nig-color-border);
	           text-align: center;
	       }

	       /* === Status Widget === */
	       .nig-status-widget {
	           position: fixed;
	           bottom: var(--nig-space-xl);
	           left: var(--nig-space-xl);
	           z-index: 100000;
	           background: var(--nig-color-bg-secondary);
	           color: var(--nig-color-text-primary);
	           padding: var(--nig-space-md) var(--nig-space-lg);
	           border-radius: var(--nig-radius-lg);
	           box-shadow: var(--nig-shadow-xl);
	           display: none;
	           align-items: center;
	           gap: var(--nig-space-md);
	           font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
	           font-size: var(--nig-font-size-sm);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	           border: 1px solid var(--nig-color-border);
	           backdrop-filter: blur(10px);
	       }

	       .nig-status-icon {
	           width: 20px;
	           height: 20px;
	           display: flex;
	           align-items: center;
	           justify-content: center;
	       }

	       .nig-status-widget.loading .nig-status-icon {
	           box-sizing: border-box;
	           border: 2px solid var(--nig-color-text-muted);
	           border-top-color: var(--nig-color-accent-primary);
	           border-radius: 50%;
	           animation: nig-spin 1s linear infinite;
	       }

	       .nig-status-widget.success {
	           background: var(--nig-color-accent-success);
	           color: white;
	           cursor: pointer;
	           border-color: var(--nig-color-accent-success);
	       }

	       .nig-status-widget.success:hover {
	           background: var(--nig-color-hover-success);
	           transform: translateY(-2px);
	           box-shadow: var(--nig-shadow-lg);
	       }

	       .nig-status-widget.error {
	           background: var(--nig-color-accent-error);
	           border-color: var(--nig-color-accent-error);
	       }

	       @keyframes nig-spin {
	           0% {
	               transform: rotate(0deg);
	           }
	           100% {
	               transform: rotate(360deg);
	           }
	       }

	       /* === Error Modal === */
	       #nig-error-reason {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           padding: var(--nig-space-lg);
	           border-radius: var(--nig-radius-md);
	           margin-top: var(--nig-space-sm);
	           max-height: 150px;
	           overflow-y: auto;
	           word-wrap: break-word;
	           overflow-wrap: break-word;
	           white-space: pre-wrap;
	           font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
	           color: var(--nig-color-text-primary);
	           line-height: 1.5;
	       }

	       .nig-error-prompt {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           padding: var(--nig-space-lg);
	           border-radius: var(--nig-radius-md);
	           margin-top: var(--nig-space-lg);
	           max-height: 200px;
	           overflow-y: auto;
	           word-break: break-word;
	           font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
	           width: 100%;
	           resize: vertical;
	           min-height: 80px;
	           color: var(--nig-color-text-primary);
	           line-height: 1.5;
	       }

	       .nig-error-actions {
	           margin-top: var(--nig-space-xl);
	       }

	       .nig-retry-btn {
	           background: var(--nig-color-accent-primary);
	           color: white;
	           padding: var(--nig-space-md) var(--nig-space-xl);
	           border: none;
	           border-radius: var(--nig-radius-md);
	           cursor: pointer;
	           font-size: var(--nig-font-size-base);
	           font-weight: 500;
	           transition: all var(--nig-transition-normal);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-retry-btn:hover {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	           box-shadow: var(--nig-shadow-md);
	       }

	       /* === Responsive Design === */
	       
	       /* Mobile First Base (up to 767px) */
	       @media (max-width: 767px) {
	           .nig-modal-content {
	               margin: var(--nig-space-md);
	               padding: var(--nig-space-xl);
	               max-height: 95vh;
	               border-radius: var(--nig-radius-lg);
	           }

	           .nig-modal-overlay {
	               padding: var(--nig-space-sm);
	           }

	           .nig-button {
	               position: fixed;
	               bottom: var(--nig-space-3xl);
	               left: 50% !important;
	               transform: translateX(-50%);
	               top: auto !important;
	               padding: var(--nig-space-sm) var(--nig-space-lg);
	               font-size: var(--nig-font-size-sm);
	               z-index: 100001;
	               min-height: 44px; /* Touch target */
	               border-radius: var(--nig-radius-lg);
	           }

	           .nig-tabs {
	               margin: 0 calc(-1 * var(--nig-space-xl)) var(--nig-space-xl) calc(-1 * var(--nig-space-xl));
	               padding: 0 var(--nig-space-xl);
	           }

	           .nig-tab {
	               padding: var(--nig-space-md) var(--nig-space-lg);
	               font-size: var(--nig-font-size-xs);
	           }

	           .nig-form-group-inline {
	               grid-template-columns: 1fr;
	               gap: var(--nig-space-md);
	           }

	           .nig-checkbox-group {
	               flex-direction: column;
	               gap: var(--nig-space-sm);
	           }

	           .nig-checkbox-group label {
	               justify-content: flex-start;
	           }

	           .nig-utilities-grid {
	               grid-template-columns: 1fr;
	               gap: var(--nig-space-lg);
	           }

	           .nig-utility-card {
	               padding: var(--nig-space-lg);
	           }

	           .nig-card-header {
	               flex-direction: column;
	               align-items: center;
	               text-align: center;
	               gap: var(--nig-space-sm);
	           }


	           .nig-card-secondary-actions {
	               grid-template-columns: 1fr;
	               gap: var(--nig-space-sm);
	           }

	           .nig-card-footer {
	               text-align: center;
	           }

	           .nig-history-cleanup {
	               flex-direction: column;
	               align-items: stretch;
	               gap: var(--nig-space-md);
	           }

	           .nig-history-cleanup input[type="number"] {
	               width: 100%;
	           }

	           .nig-status-widget {
	               bottom: var(--nig-space-lg);
	               left: var(--nig-space-md);
	               right: var(--nig-space-md);
	               padding: var(--nig-space-md);
	               font-size: var(--nig-font-size-xs);
	           }

	           .nig-image-gallery {
	               grid-template-columns: 1fr;
	               gap: var(--nig-space-lg);
	           }

	           .nig-modal-content h2 {
	               font-size: var(--nig-font-size-xl);
	               padding-right: 48px; /* Space for close button */
	           }
	       }

	       /* Tablet (768px to 1023px) */
	       @media (min-width: 768px) and (max-width: 1023px) {
	           .nig-modal-content {
	               max-width: 700px;
	           }

	           .nig-utilities-grid {
	               grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	           }

	           .nig-image-gallery {
	               grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
	           }
	       }

	       /* Desktop (1024px and up) */
	       @media (min-width: 1024px) {
	           .nig-modal-content {
	               max-width: 1000px;
	           }

	           .nig-utilities-grid {
	               grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	           }

	           .nig-image-gallery {
	               grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	           }

	           .nig-form-group-inline {
	               grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	           }

	           /* Enhanced hover states for desktop */
	           .nig-tab:hover {
	               background: var(--nig-color-bg-tertiary);
	           }

	           .nig-history-item:hover {
	               transform: translateY(-1px);
	           }
	       }

	       /* Large Desktop (1280px and up) */
	       @media (min-width: 1280px) {
	           .nig-modal-content {
	               max-width: 1200px;
	           }

	           .nig-utilities-grid {
	               grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
	           }
	       }

	       /* Print Styles */
	       @media print {
	           .nig-modal-overlay,
	           .nig-status-widget,
	           .nig-button {
	               display: none !important;
	           }

	           .nig-modal-content {
	               box-shadow: none;
	               border: 1px solid #000;
	               background: white;
	               color: black;
	           }
	       }

	       /* High Contrast Mode Support */
	       @media (prefers-contrast: high) {
	           :root {
	               --nig-color-bg-primary: #000000;
	               --nig-color-bg-secondary: #1a1a1a;
	               --nig-color-bg-tertiary: #2a2a2a;
	               --nig-color-text-primary: #ffffff;
	               --nig-color-border: #666666;
	           }
	       }

	       /* Reduced Motion Support */
	       @media (prefers-reduced-motion: reduce) {
	           *,
	           *::before,
	           *::after {
	               animation-duration: 0.01ms !important;
	               animation-iteration-count: 1 !important;
	               transition-duration: 0.01ms !important;
	           }
	       }

	       /* === Panel Configuration Grid Layout === */
	       .nig-config-grid {
	           display: grid;
	           grid-template-columns: 1fr;
	           gap: var(--nig-space-xl);
	       }

	       .nig-config-section {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-xl);
	       }

	       .nig-provider-container {
	           display: grid;
	           gap: var(--nig-space-lg);
	       }

	       .nig-provider-header {
	           margin-bottom: var(--nig-space-lg);
	           padding-bottom: var(--nig-space-lg);
	           border-bottom: 1px solid var(--nig-color-border);
	       }

	       .nig-provider-header h3 {
	           margin: 0 0 var(--nig-space-sm) 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-lg);
	           font-weight: 600;
	           display: flex;
	           align-items: center;
	           gap: var(--nig-space-sm);
	       }

	       .nig-provider-header p {
	           margin: 0;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           line-height: 1.5;
	       }

	       .nig-provider-controls {
	           display: grid;
	           grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
	           gap: var(--nig-space-lg);
	           margin-bottom: var(--nig-space-lg);
	       }

	       .nig-provider-settings {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-xl);
	           transition: all var(--nig-transition-normal);
	       }

	       .nig-provider-settings:hover {
	           border-color: var(--nig-color-border-light);
	           box-shadow: var(--nig-shadow-sm);
	       }

	       .nig-form-grid {
	           display: grid;
	           grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	           gap: var(--nig-space-lg);
	           margin-bottom: var(--nig-space-lg);
	       }

	       /* === Styling Tab Layout === */
	       .nig-styling-container {
	           display: grid;
	           gap: var(--nig-space-xl);
	       }

	       .nig-styling-intro {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-lg);
	           margin-bottom: var(--nig-space-lg);
	       }

	       .nig-styling-intro p {
	           margin: 0;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           line-height: 1.6;
	       }

	       .nig-style-grid {
	           display: grid;
	           grid-template-columns: 1fr;
	           gap: var(--nig-space-xl);
	       }

	       .nig-style-section {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-xl);
	       }

	       .nig-section-header {
	           margin-bottom: var(--nig-space-lg);
	           padding-bottom: var(--nig-space-lg);
	           border-bottom: 1px solid var(--nig-color-border);
	       }

	       .nig-section-header h4 {
	           margin: 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-lg);
	           font-weight: 600;
	       }

	       /* === History Tab Layout === */
	       .nig-history-container {
	           display: grid;
	           gap: var(--nig-space-xl);
	       }

	       .nig-history-cleanup {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-xl);
	           display: grid;
	           gap: var(--nig-space-lg);
	       }

	       .nig-cleanup-info h4 {
	           margin: 0 0 var(--nig-space-sm) 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-lg);
	           font-weight: 600;
	       }

	       .nig-cleanup-info p {
	           margin: 0;
	           color: var(--nig-color-text-secondary);
	           font-size: var(--nig-font-size-sm);
	           line-height: 1.5;
	       }

	       .nig-cleanup-controls {
	           display: flex;
	           align-items: center;
	           gap: var(--nig-space-md);
	           flex-wrap: wrap;
	       }

	       .nig-cleanup-controls label {
	           color: var(--nig-color-text-primary);
	           font-weight: 500;
	           font-size: var(--nig-font-size-sm);
	       }

	       .nig-cleanup-controls input[type="number"] {
	           width: 80px;
	           padding: var(--nig-space-sm);
	           background: var(--nig-color-bg-primary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-md);
	           color: var(--nig-color-text-primary);
	       }

	       /* === Enhancement Status & Priority Styles === */
	       .nig-enhancement-status {
	           display: inline-flex;
	           align-items: center;
	           gap: var(--nig-space-sm);
	           padding: var(--nig-space-xs) var(--nig-space-sm);
	           border-radius: var(--nig-radius-md);
	           font-size: var(--nig-font-size-xs);
	           font-weight: 500;
	           margin-left: var(--nig-space-md);
	       }
	       
	       .nig-enhancement-status.provider-priority {
	           background: var(--nig-color-accent-warning);
	           color: white;
	       }
	       
	       .nig-enhancement-status.external-ai {
	           background: var(--nig-color-accent-primary);
	           color: white;
	       }
	       
	       .nig-enhancement-status.disabled {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-muted);
	       }
	       
	       .nig-status-indicator {
	           width: 8px;
	           height: 8px;
	           border-radius: 50%;
	           background: #4af028;
	           opacity: 0.8;
	       }
	       
	       .nig-provider-priority-info {
	           background: var(--nig-color-bg-primary);
	           border: 1px solid var(--nig-color-accent-warning);
	           border-radius: var(--nig-radius-lg);
	            padding: var(--nig-space-lg);
	           margin-bottom: var(--nig-space-lg);
	           display: flex;
	           flex-direction: column;
	           gap: var(--nig-space-md);
	       }
	       
	       .nig-priority-header {
	           display: flex;
	           align-items: center;
	           gap: var(--nig-space-sm);
	           color: var(--nig-color-accent-warning);
	           font-weight: 600;
	           font-size: var(--nig-font-size-sm);
	       }
	       
	       .nig-override-btn {
	           background: var(--nig-color-accent-primary);
	           color: white;
	           border: none;
	           border-radius: var(--nig-radius-md);
	            padding: var(--nig-space-sm) var(--nig-space-md);
	           font-size: var(--nig-font-size-sm);
	           cursor: pointer;
	           transition: all var(--nig-transition-normal);
	           align-self: flex-start;
	       }
	       
	       .nig-override-btn:hover {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	       }
	       
	       /* === Enhancement Preview Styles === */
	       .nig-enhancement-preview {
	           background: var(--nig-color-bg-primary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-lg);
	           padding: var(--nig-space-lg);
	           margin-top: var(--nig-space-lg);
	       }
	       
	       .nig-preview-container {
	           display: grid;
	           grid-template-columns: 1fr auto 1fr;
	           gap: var(--nig-space-lg);
	           align-items: start;
	           margin-bottom: var(--nig-space-lg);
	       }
	       
	       .nig-preview-section {
	           display: flex;
	           flex-direction: column;
	           gap: var(--nig-space-sm);
	       }
	       
	       .nig-preview-section h5 {
	           margin: 0;
	           color: var(--nig-color-text-primary);
	           font-size: var(--nig-font-size-sm);
	           font-weight: 600;
	           text-transform: uppercase;
	           letter-spacing: 0.025em;
	       }
	       
	       .nig-preview-arrow {
	           display: flex;
	           align-items: center;
	           justify-content: center;
	           color: var(--nig-color-accent-primary);
	           padding: var(--nig-space-sm);
	       }
	       
	       .nig-prompt-display {
	           background: var(--nig-color-bg-tertiary);
	           border: 1px solid var(--nig-color-border);
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-md);
	           font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
	           font-size: var(--nig-font-size-xs);
	           line-height: 1.4;
	           color: var(--nig-color-text-secondary);
	           max-height: 120px;
	           overflow-y: auto;
	           word-wrap: break-word;
	           white-space: pre-wrap;
	       }
	       
	       .nig-test-enhancement-btn {
	           background: var(--nig-color-accent-primary);
	           color: white;
	           border: none;
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-sm) var(--nig-space-lg);
	           font-size: var(--nig-font-size-sm);
	           cursor: pointer;
	           transition: all var(--nig-transition-normal);
	           display: flex;
	           align-items: center;
	           gap: var(--nig-space-sm);
	           width: 100%;
	           justify-content: center;
	       }
	       
	       .nig-test-enhancement-btn:hover:not(:disabled) {
	           background: var(--nig-color-hover-primary);
	           transform: translateY(-1px);
	       }
	       
	       .nig-test-enhancement-btn:disabled {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-muted);
	           cursor: not-allowed;
	           transform: none;
	       }
	       
	       /* === Enhancement Settings States === */
	       .nig-enhancement-settings {
	           transition: all var(--nig-transition-normal);
	       }
	       
	       .nig-enhancement-settings.disabled {
	           opacity: 0.5;
	           pointer-events: none;
	       }
	       
	       .nig-enhancement-settings.disabled .nig-form-group input,
	       .nig-enhancement-settings.disabled .nig-form-group select,
	       .nig-enhancement-settings.disabled .nig-form-group textarea {
	           background: var(--nig-color-bg-tertiary);
	           color: var(--nig-color-text-muted);
	           cursor: not-allowed;
	       }
	       
	       /* === Template Button Styles === */
	       .nig-template-btn {
	           background: var(--nig-color-bg-elevated);
	           border: 1px solid var(--nig-color-border);
	           color: var(--nig-color-text-secondary);
	           border-radius: var(--nig-radius-md);
	           padding: var(--nig-space-xs) var(--nig-space-sm);
	           font-size: var(--nig-font-size-xs);
	           cursor: pointer;
	           transition: all var(--nig-transition-fast);
	       }
	       
	       .nig-template-btn:hover {
	           background: var(--nig-color-accent-primary);
	           color: white;
	           border-color: var(--nig-color-accent-primary);
	       }
	       
	       /* === Mobile Enhancement Styles === */
	       @media (max-width: 767px) {
	           .nig-preview-container {
	               grid-template-columns: 1fr;
	               gap: var(--nig-space-md);
	           }
	           
	           .nig-preview-arrow {
	               transform: rotate(90deg);
	               justify-self: center;
	           }
	           
	           .nig-enhancement-status {
	               margin-left: 0;
	               margin-top: var(--nig-space-sm);
	               align-self: flex-start;
	           }
	       }
	       
	       /* === Enhanced Responsive Grid === */
	       @media (min-width: 768px) {
	           .nig-config-grid {
	               grid-template-columns: 1fr;
	           }

	           .nig-provider-controls {
	               grid-template-columns: repeat(2, 1fr);
	           }

	           .nig-form-grid {
	               grid-template-columns: repeat(2, 1fr);
	           }

	           .nig-style-grid {
	               grid-template-columns: 1fr;
	           }

	           .nig-cleanup-controls {
	               justify-content: flex-start;
	           }
	       }

	       @media (min-width: 1024px) {
	           .nig-config-grid {
	               grid-template-columns: 1fr;
	           }

	           .nig-provider-controls {
	               grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	           }

	           .nig-form-grid {
	               grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
	           }

	           .nig-style-grid {
	               grid-template-columns: 1fr;
	           }

	           .nig-provider-settings:hover {
	               transform: translateY(-2px);
	               box-shadow: var(--nig-shadow-md);
	           }
	       }

	       @media (min-width: 1280px) {
	           .nig-config-grid {
	               grid-template-columns: 1fr;
	           }

	           .nig-provider-controls {
	               grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	           }

	           .nig-style-grid {
	               grid-template-columns: repeat(2, 1fr);
	           }

	           .nig-styling-container {
	               grid-template-columns: 1fr;
	           }
	       }

	       /* === File Input Styling === */
	       input[type="file"] {
	           border: 2px dashed var(--nig-color-border);
	           background: var(--nig-color-bg-primary);
	           padding: var(--nig-space-xl);
	           border-radius: var(--nig-radius-lg);
	           color: var(--nig-color-text-secondary);
	           transition: all var(--nig-transition-normal);
	           cursor: pointer;
	       }

	       input[type="file"]:hover {
	           border-color: var(--nig-color-accent-primary);
	           background: var(--nig-color-bg-elevated);
	       }

	       input[type="file"]:focus {
	           outline: none;
	           border-color: var(--nig-color-accent-primary);
	           box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
	       }
	   `

	// --- 2. DEFAULTS & STORAGE HELPERS ---
	const PROMPT_CATEGORIES = [
		{name: 'None', description: 'No additional styling will be added to your prompt.', subStyles: []},
		{
			name: 'Anime',
			description: 'Blends Japanese animation with global twists. Sub-styles often mix eras, genres, or crossovers for dynamic outputs.',
			subStyles: [
				{name: 'None', value: 'none', description: 'Use only the main style name as a prefix (e.g., "Anime style, ...").'},
				{
					name: 'Studio Ghibli-Inspired',
					description: 'Whimsical, nature-focused fantasy with soft lines and emotional depth.',
					value: 'Studio Ghibli style, '
				},
				{
					name: 'Cyberpunk Anime',
					description: 'Neon-lit dystopias with high-tech mechs and gritty urban vibes.',
					value: 'Cyberpunk anime style, '
				},
				{
					name: 'Semi-Realistic Anime',
					description: 'Blends lifelike proportions with expressive anime eyes and shading.',
					value: 'Semi-realistic anime style, '
				},
				{name: 'Mecha', description: 'Giant robots and mechanical suits in epic battles.', value: 'Mecha anime style, '},
				{
					name: 'Dynamic Action',
					description: 'High-energy movements with power effects and intense expressions.',
					value: 'Dynamic action anime style, '
				},
				{
					name: 'Soft Romantic',
					description: 'Emotional interactions with gentle colors and sparkling accents.',
					value: 'Soft romantic anime style, '
				},
				{
					name: 'Dark Fantasy Anime',
					description: 'Grim, horror-tinged worlds with demons and shadows.',
					value: 'Dark fantasy anime style, '
				},
				{
					name: 'Retro 80s Anime',
					description: 'Vintage cel-shaded look with bold lines and synth vibes.',
					value: '80s retro anime style, '
				},
				{
					name: 'Portal Fantasy',
					description: 'World-crossing elements with magical adaptations and RPG motifs.',
					value: 'Portal fantasy anime style, '
				},
				{
					name: 'Slice-of-Life',
					description: 'Everyday moments with relatable characters and cozy vibes.',
					value: 'Slice-of-life anime style, '
				},
				{
					name: 'Serialized Narrative',
					description: 'Panel-like compositions for ongoing story flows.',
					value: 'Serialized narrative anime style, '
				},
				{
					name: 'Group Dynamic',
					description: 'Interactions among multiple characters with balanced focus.',
					value: 'Group dynamic anime style, '
				}
			]
		},
		{
			name: 'Realism/Photorealism',
			description: 'Excels in portraits and scenes mimicking photography, with sub-styles varying by subject or technique.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Realism/Photorealism style, ...").'
				},
				{
					name: 'Hyperrealism',
					description: 'Ultra-detailed, almost tangible textures and lighting.',
					value: 'Hyperrealistic, '
				},
				{
					name: 'Cinematic Realism',
					description: 'Film-like depth with dramatic angles and color grading.',
					value: 'Cinematic realism, '
				},
				{
					name: 'Portrait Photorealism',
					description: 'Human faces with natural skin, eyes, and expressions.',
					value: 'Portrait photorealism, '
				},
				{
					name: 'Architectural Realism',
					description: 'Precise building renders with environmental details.',
					value: 'Architectural realism, '
				},
				{
					name: 'Nature Photorealism',
					description: 'Verdant landscapes with dew and foliage intricacies.',
					value: 'Nature photorealism, '
				},
				{
					name: 'Close-Up Detail',
					description: 'Intimate views highlighting textures and fine elements.',
					value: 'Close-up realistic style, '
				},
				{
					name: 'Historical Realism',
					description: 'Period-accurate clothing and settings with grit.',
					value: 'Historical realism, '
				},
				{name: 'Urban Realism', description: 'Bustling city life with crowds and neon realism.', value: 'Urban realism, '},
				{name: 'Stylized Realism', description: 'Subtle artistic tweaks on photoreal bases.', value: 'Stylized realism, '},
				{
					name: 'Documentary Style',
					description: 'Raw, unpolished scenes like news photography.',
					value: 'Documentary photo style, '
				},
				{
					name: 'Object Focus Realism',
					description: 'Clear, highlighted items with neutral lighting.',
					value: 'Object-focused realism, '
				},
				{
					name: 'Wildlife Realism',
					description: 'Animals in habitats with fur and feather fidelity.',
					value: 'Wildlife realism, '
				},
				{
					name: 'Detailed Portrait',
					description: 'Lifelike faces with expressive features.',
					value: 'Detailed portrait realism, '
				},
				{
					name: 'Environmental Immersion',
					description: 'Rich settings enveloping subjects.',
					value: 'Environmental immersion realism, '
				}
			]
		},
		{
			name: 'Fantasy',
			description: 'Epic worlds of magic and myth, with sub-styles spanning tones from whimsical to grim.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Fantasy style, ...").'
				},
				{
					name: 'High Fantasy',
					description: 'Medieval realms with elves, dragons, and quests.',
					value: 'High fantasy art, '
				},
				{
					name: 'Dark Fantasy',
					description: 'Grimdark horror with undead and moral ambiguity.',
					value: 'Dark fantasy art, '
				},
				{name: 'Urban Fantasy', description: 'Magic in modern cities, like hidden witches.', value: 'Urban fantasy art, '},
				{
					name: 'Steampunk Fantasy',
					description: 'Victorian tech with gears and airships.',
					value: 'Steampunk fantasy style, '
				},
				{
					name: 'Fairy Tale',
					description: 'Whimsical tales with enchanted woods and creatures.',
					value: 'Fairy tale illustration style, '
				},
				{
					name: 'Heroic Adventure',
					description: 'Bold explorers with raw magic and ancient relics.',
					value: 'Heroic adventure fantasy art, '
				},
				{
					name: 'Creature Emphasis',
					description: 'Fantastical beings in natural or enchanted environments.',
					value: 'Creature-focused fantasy art, '
				},
				{
					name: 'Ethereal Grace',
					description: 'Elegant figures in luminous, forested settings.',
					value: 'Ethereal grace fantasy style, '
				},
				{
					name: 'Rugged Craftsmanship',
					description: 'Stout builders in forged, underground realms.',
					value: 'Rugged craftsmanship fantasy style, '
				},
				{name: 'Gothic Fantasy', description: 'Haunted castles with vampires and storms.', value: 'Gothic fantasy art, '},
				{
					name: 'Beast Majesty',
					description: 'Powerful scaled creatures in dramatic poses.',
					value: 'Majestic beast fantasy art, '
				},
				{
					name: 'Celestial Fantasy',
					description: 'Starry realms with gods and floating islands.',
					value: 'Celestial fantasy art, '
				},
				{
					name: 'Oriental Myth',
					description: 'Asian folklore elements with harmonious nature.',
					value: 'Oriental myth fantasy style, '
				},
				{
					name: 'Treasure Hunt Vibe',
					description: 'Exploratory scenes with hidden wonders.',
					value: 'Treasure hunt fantasy art, '
				}
			]
		},
		{
			name: 'Sci-Fi',
			description: 'Futuristic visions from gritty cyber worlds to cosmic explorations.',
			subStyles: [
				{name: 'None', value: 'none', description: 'Use only the main style name as a prefix (e.g., "Sci-Fi style, ...").'},
				{name: 'Cyberpunk', description: 'Neon dystopias with hackers and megacorps.', value: 'Cyberpunk style, '},
				{name: 'Retro-Futurism', description: '1950s optimism with ray guns and chrome.', value: 'Retro-futurism, '},
				{name: 'Biopunk', description: 'Organic tech with genetic mutations.', value: 'Biopunk sci-fi style, '},
				{
					name: 'Interstellar Epic',
					description: 'Vast cosmic tales with diverse species and ships.',
					value: 'Interstellar epic sci-fi art, '
				},
				{
					name: 'Mechanical Suit',
					description: 'Armored machines in high-tech conflicts.',
					value: 'Mechanical suit sci-fi style, '
				},
				{name: 'Post-Human', description: 'Cyborgs and AI in evolved societies.', value: 'Post-human sci-fi art, '},
				{
					name: 'Hard Sci-Fi',
					description: 'Physics-based realism with tech schematics.',
					value: 'Hard sci-fi illustration, '
				},
				{name: 'Dieselpunk', description: '1930s grit with riveted machines.', value: 'Dieselpunk style, '},
				{name: 'Astro-Mythology', description: 'Space gods and cosmic myths.', value: 'Astro-mythology art, '},
				{name: 'Eco-Sci-Fi', description: 'Post-apocalypse with bio-domes.', value: 'Eco-sci-fi art, '},
				{
					name: 'Survival Wasteland',
					description: 'Harsh, ruined landscapes with resilient figures.',
					value: 'Survival wasteland sci-fi style, '
				},
				{
					name: 'Cosmic Discovery',
					description: 'Unknown worlds with exploratory tech.',
					value: 'Cosmic discovery sci-fi art, '
				}
			]
		},
		{
			name: 'Retro/Vintage',
			description: 'Nostalgic aesthetics from bygone eras, revived with AI flair.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Retro/Vintage style, ...").'
				},
				{name: 'Art Deco', description: 'Geometric luxury with gold and symmetry.', value: 'Art Deco style, '},
				{name: 'Art Nouveau', description: 'Flowing organic lines and floral motifs.', value: 'Art Nouveau style, '},
				{name: 'Vintage Poster', description: 'Bold typography and illustrative ads.', value: 'Vintage poster style, '},
				{name: 'Chromolithography', description: 'Vibrant, printed color layers from 1900s.', value: 'Chromolithography, '},
				{name: 'Baroque', description: 'Ornate drama with rich drapery.', value: 'Baroque painting style, '},
				{name: 'Ukiyo-e', description: 'Japanese woodblock prints with flat colors.', value: 'Ukiyo-e style, '},
				{name: '1950s Retro', description: 'Atomic age optimism with pastels.', value: '1950s retro style, '},
				{
					name: 'Playful Figure',
					description: 'Charming, stylized poses with vintage flair.',
					value: 'Playful vintage figure style, '
				},
				{name: 'Edwardian', description: 'Lacy elegance with soft pastels.', value: 'Edwardian era style, '},
				{name: 'Mid-Century Modern', description: 'Clean lines and bold geometrics.', value: 'Mid-century modern style, '},
				{
					name: 'Ink Scroll',
					description: 'Brush-like lines evoking ancient manuscripts.',
					value: 'Ink scroll vintage style, '
				}
			]
		},
		{
			name: 'Surrealism',
			description: 'Dreamlike distortions challenging reality, inspired by masters.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Surrealism style, ...").'
				},
				{
					name: 'Fluid Distortion',
					description: 'Melting forms and impossible blends.',
					value: 'Fluid distortion surrealism, '
				},
				{
					name: 'Paradoxical Objects',
					description: 'Everyday items in illogical arrangements.',
					value: 'Paradoxical object surrealism, '
				},
				{
					name: 'Ernst Collage Surreal',
					description: 'Layered fragments for uncanny narratives.',
					value: 'Ernst collage surrealism, '
				},
				{
					name: 'Kahlo Autobiographical',
					description: 'Personal symbolism with thorny motifs.',
					value: 'Frida Kahlo style surrealism, '
				},
				{name: 'Biomorphic Surreal', description: 'Organic, creature-like hybrids.', value: 'Biomorphic surrealism, '},
				{
					name: 'Dreamlike Landscapes',
					description: 'Floating islands and inverted gravity.',
					value: 'Dreamlike surreal landscape, '
				},
				{
					name: 'Freudian Symbolic',
					description: 'Subconscious icons like eyes and stairs.',
					value: 'Freudian symbolic surrealism, '
				},
				{
					name: 'Pop Surrealism',
					description: 'Whimsical grotesquery with candy colors.',
					value: 'Pop surrealism, lowbrow art, '
				},
				{name: 'Hyper-Surreal', description: 'Exaggerated distortions in vivid detail.', value: 'Hyper-surrealism, '},
				{name: 'Eco-Surreal', description: 'Nature twisted with human elements.', value: 'Eco-surrealism, '},
				{name: 'Mechanical Surreal', description: 'Machines fused with flesh.', value: 'Mechanical surrealism, '},
				{
					name: 'Inner Vision',
					description: 'Symbolic inner thoughts with blended realities.',
					value: 'Inner vision surrealism, '
				}
			]
		},
		{
			name: 'Cartoon/Illustration',
			description: 'Exaggerated, narrative-driven visuals for fun and storytelling.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Cartoon/Illustration style, ...").'
				},
				{
					name: 'Pixar 3D',
					description: 'Polished, expressive CG with emotional arcs.',
					value: 'Pixar 3D animation style, '
				},
				{
					name: 'Disney Classic',
					description: 'Hand-drawn whimsy with fluid animation.',
					value: 'Classic Disney animation style, '
				},
				{name: 'DreamWorks', description: 'Edgy humor with detailed backgrounds.', value: 'DreamWorks animation style, '},
				{
					name: 'Adventure Time',
					description: 'Surreal candy lands with bold shapes.',
					value: 'Adventure Time cartoon style, '
				},
				{
					name: 'Simpsons',
					description: 'Yellow-skinned satire with clean outlines.',
					value: 'The Simpsons cartoon style, '
				},
				{
					name: 'Rick and Morty',
					description: 'Sci-fi absurdity with warped perspectives.',
					value: 'Rick and Morty cartoon style, '
				},
				{
					name: 'Narrative Panel',
					description: 'Sequential art with shaded storytelling.',
					value: 'Narrative panel illustration style, '
				},
				{
					name: 'Whimsical Illustration',
					description: 'Gentle, colorful drawings for light-hearted scenes.',
					value: 'Whimsical illustration style, '
				},
				{name: 'Webtoon', description: 'Vertical scroll with vibrant digital ink.', value: 'Webtoon style, '},
				{
					name: 'Manhua Flow',
					description: 'Dynamic lines and vibrant digital shading.',
					value: 'Manhua flow illustration style, '
				},
				{
					name: 'Cover Art Focus',
					description: 'Striking compositions for thematic highlights.',
					value: 'Cover art illustration, '
				}
			]
		},
		{
			name: 'Traditional Painting',
			description: 'Emulates historical mediums like oils and watercolors for timeless appeal.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Traditional Painting style, ...").'
				},
				{
					name: 'Impressionism',
					description: 'Loose brushstrokes capturing light moments.',
					value: 'Impressionist painting, '
				},
				{
					name: 'Renaissance',
					description: 'Balanced compositions with chiaroscuro.',
					value: 'Renaissance painting style, '
				},
				{name: 'Oil Painting', description: 'Rich, layered textures with glazing.', value: 'Oil painting, '},
				{name: 'Watercolor', description: 'Translucent washes for ethereal softness.', value: 'Watercolor painting, '},
				{name: 'Baroque', description: 'Dramatic tenebrism and opulent details.', value: 'Baroque painting, '},
				{name: 'Romanticism', description: 'Emotional storms and heroic figures.', value: 'Romanticism painting, '},
				{name: 'Pointillism', description: 'Dot-based color mixing for vibrancy.', value: 'Pointillism style, '},
				{name: 'Fresco', description: 'Mural-like with aged plaster effects.', value: 'Fresco painting style, '},
				{name: 'Encaustic', description: 'Waxy, heated layers for luminous depth.', value: 'Encaustic painting, '},
				{name: 'Acrylic', description: 'Bold, matte finishes with quick drying.', value: 'Acrylic painting, '},
				{name: 'Gouache', description: 'Opaque vibrancy like matte poster paint.', value: 'Gouache painting, '},
				{name: 'Sumi-e', description: 'Minimalist ink washes for Zen simplicity.', value: 'Sumi-e ink wash painting, '},
				{
					name: 'Oriental Brushwork',
					description: 'Minimalist inks for balanced compositions.',
					value: 'Oriental brushwork style, '
				},
				{name: 'Era Line Art', description: 'Detailed etchings for historical depth.', value: 'Era line art traditional, '}
			]
		},
		{
			name: 'Digital Art',
			description: 'Modern, tech-infused creations from pixels to vectors.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Digital Art style, ...").'
				},
				{
					name: 'Vector Illustration',
					description: 'Scalable, flat colors with clean paths.',
					value: 'Vector illustration, '
				},
				{
					name: 'Blended Landscape',
					description: 'Layered digital environments for immersive backdrops.',
					value: 'Blended digital landscape, '
				},
				{name: 'Neon Glow', description: 'Vibrant outlines with electric luminescence.', value: 'Neon glow digital art, '},
				{name: 'Holographic', description: 'Shimmering, 3D projections with refractions.', value: 'Holographic style, '},
				{
					name: 'World-Building Sketch',
					description: 'Conceptual layers for expansive scenes.',
					value: 'World-building digital sketch, '
				},
				{
					name: 'Community Render',
					description: 'Polished digital interpretations of characters.',
					value: 'Community render digital style, '
				}
			]
		},
		{
			name: 'Wuxia/Xianxia',
			description: 'Eastern-inspired martial and spiritual themes with energy flows, ancient motifs, and harmonious or intense atmospheres.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Wuxia/Xianxia style, ...").'
				},
				{
					name: 'Qi Energy Flow',
					description: 'Subtle auras and internal power visualizations.',
					value: 'Qi energy flow style, '
				},
				{name: 'Martial Grace', description: 'Fluid poses and disciplined movements.', value: 'Martial grace style, '},
				{
					name: 'Spiritual Realm',
					description: 'Misty, elevated worlds with ethereal elements.',
					value: 'Spiritual realm style, '
				},
				{
					name: 'Ancient Sect Aesthetic',
					description: 'Traditional architecture and robed figures.',
					value: 'Ancient sect aesthetic, '
				},
				{
					name: 'Demonic Shadow',
					description: 'Darkened energies and mysterious silhouettes.',
					value: 'Demonic shadow style, '
				},
				{
					name: 'Dynasty Elegance',
					description: 'Silk textures and jade accents in historical tones.',
					value: 'Dynasty elegance style, '
				}
			]
		},
		{
			name: 'Romance',
			description: 'Tender or passionate human connections with soft lighting, expressions, and atmospheric details.',
			subStyles: [
				{
					name: 'None',
					value: 'none',
					description: 'Use only the main style name as a prefix (e.g., "Romance style, ...").'
				},
				{
					name: 'Gentle Intimacy',
					description: 'Close, affectionate moments with warm hues.',
					value: 'Gentle intimacy romance style, '
				},
				{
					name: 'Urban Affection',
					description: 'Modern settings with subtle romantic gestures.',
					value: 'Urban affection style, '
				},
				{
					name: 'Enchanted Bond',
					description: 'Magical elements enhancing emotional ties.',
					value: 'Enchanted bond romance style, '
				},
				{
					name: 'Tension Build',
					description: 'Subtle conflicts leading to connection.',
					value: 'Tension build romance style, '
				},
				{
					name: 'Blushing Softness',
					description: 'Delicate emotions with pastel accents.',
					value: 'Blushing softness style, '
				},
				{
					name: 'Melancholic Yearning',
					description: 'Poignant separations with evocative moods.',
					value: 'Melancholic yearning romance art, '
				}
			]
		}
	]

	const TOP_MODELS = [
		{name: 'Deliberate', desc: 'Versatile, high-quality realism and detail.'},
		{name: 'Anything Diffusion', desc: 'Anime-style specialist.'},
		{name: "ICBINP - I Can't Believe It's Not Photography", desc: 'Photorealistic focus; excels in lifelike portraits.'},
		{name: 'stable_diffusion', desc: 'The classic base model; reliable all-rounder.'},
		{name: 'AlbedoBase XL (SDXL)', desc: 'SDXL variant; strong for high-res, detailed scenes.'},
		{name: 'Nova Anime XL', desc: 'Anime and illustration; vibrant, dynamic characters.'},
		{name: 'Dreamshaper', desc: 'Creative and dreamy outputs; good for artistic concepts.'},
		{name: 'Hentai Diffusion', desc: 'NSFW/anime erotica specialist.'},
		{name: 'CyberRealistic Pony', desc: 'Realistic with a cyberpunk twist.'},
		{name: 'Flux.1-Schnell fp8 (Compact)', desc: 'Newer, fast-generation model for quick results.'}
	]
	const DEFAULTS = {
		selectedProvider: 'Pollinations',
		loggingEnabled: false,
		// Prompt Styling
		mainPromptStyle: 'None',
		subPromptStyle: 'none',
		customStyleEnabled: false,
		customStyleText: '',
		// AI Prompt Enhancement
		enhancementEnabled: false,
		enhancementProvider: 'gemini', // 'gemini', 'disabled'
		enhancementApiKey: '',
		enhancementModel: 'models/gemini-2.5-pro',
		enhancementTemplate: 'Convert this text into a focused visual description for image generation. Extract key visual elements (characters, setting, mood, style) and describe them as a direct prompt without narrative elements, dialogue, or markdown formatting. Keep it concise and focused on what can be visually rendered. End with quality boosters like "highly detailed, sharp focus, 8K resolution, masterpiece. Generated Prompt Structure: Start with core subjects, layer in scene/mood, then style/technicals:',
		enhancementOverrideProvider: false,
		enhancementLastStatus: 'disabled',
		// Global Negative Prompting
		enableNegPrompt: true,
		globalNegPrompt: 'ugly, blurry, deformed, disfigured, poor details, bad anatomy, low quality',
		// Google
		googleApiKey: '',
		model: 'imagen-4.0-generate-001',
		numberOfImages: 1,
		imageSize: '1024',
		aspectRatio: '1:1',
		personGeneration: 'allow_adult',
		// AI Horde
		aiHordeApiKey: '0000000000',
		aiHordeModel: 'AlbedoBase XL (SDXL)',
		aiHordeSampler: 'k_dpmpp_2m',
		aiHordeSteps: 25,
		aiHordeCfgScale: 7,
		aiHordeWidth: 512,
		aiHordeHeight: 512,
		aiHordePostProcessing: [],
		aiHordeSeed: '',
		// Pollinations.ai
		pollinationsModel: 'flux',
		pollinationsWidth: 512,
		pollinationsHeight: 512,
		pollinationsSeed: '',
		pollinationsEnhance: true,
		pollinationsNologo: false,
		pollinationsPrivate: false,
		pollinationsSafe: true,
		pollinationsToken: '',
		// OpenAI Compatible
		openAICompatProfiles: {},
		openAICompatActiveProfileUrl: '',
		openAICompatModelManualInput: false
	}

	// --- Enhanced Logging System ---
	let loggingEnabled = DEFAULTS.loggingEnabled
	let enhancementLogHistory = []

	async function updateLoggingStatus() {
		loggingEnabled = await GM_getValue('loggingEnabled', DEFAULTS.loggingEnabled)
	}

	// Enhanced logging with levels and timestamps
	function log(level, category, message, data = null) {
		if (!loggingEnabled) return

		const timestamp = new Date().toISOString()
		const logEntry = {
			timestamp,
			level,
			category,
			message,
			data
		}

		// Console logging with color coding
		const prefix = `[NIG-${level.toUpperCase()}]`
		const categoryPrefix = `[${category}]`
		const coloredPrefix = level === 'error' ? `%c${prefix}` : level === 'warn' ? `%c${prefix}` : level === 'debug' ? `%c${prefix}` : `%c${prefix}`

		const style = level === 'error' ? 'color: #ef4444' : level === 'warn' ? 'color: #f59e0b' : level === 'debug' ? 'color: #8b5cf6' : 'color: #6366f1'

		if (data) {
			console.log(coloredPrefix, style, categoryPrefix, message, data)
		} else {
			console.log(coloredPrefix, style, categoryPrefix, message)
		}

		// Store enhancement logs for user visibility
		if (category === 'ENHANCEMENT') {
			enhancementLogHistory.unshift(logEntry)
			// Keep only last 50 enhancement logs
			if (enhancementLogHistory.length > 50) {
				enhancementLogHistory = enhancementLogHistory.slice(0, 50)
			}
			// Store in persistent storage
			GM_setValue('enhancementLogHistory', JSON.stringify(enhancementLogHistory.slice(0, 20)))
		}
	}

	// Convenience methods for different log levels
	function logInfo(category, message, data) {
		log('info', category, message, data)
	}
	function logDebug(category, message, data) {
		log('debug', category, message, data)
	}
	function logWarn(category, message, data) {
		log('warn', category, message, data)
	}
	function logError(category, message, data) {
		log('error', category, message, data)
	}

	// Load enhancement log history from storage
	async function loadEnhancementLogHistory() {
		try {
			const stored = await GM_getValue('enhancementLogHistory', '[]')
			enhancementLogHistory = JSON.parse(stored)
			logDebug('LOG', `Loaded ${enhancementLogHistory.length} enhancement log entries from storage`)
		} catch (e) {
			logError('LOG', 'Failed to load enhancement log history', e)
			enhancementLogHistory = []
		}
	}

	// Enhancement log management functions
	async function getEnhancementLogHistory() {
		await loadEnhancementLogHistory()
		return enhancementLogHistory
	}

	function clearEnhancementLogs() {
		enhancementLogHistory = []
		GM_setValue('enhancementLogHistory', '[]')
		logInfo('LOG', 'Enhancement logs cleared by user')
	}

	function formatLogEntry(entry) {
		const time = new Date(entry.timestamp).toLocaleString()
		const levelColors = {
			ERROR: '#ef4444',
			WARN: '#f59e0b',
			INFO: '#6366f1',
			DEBUG: '#8b5cf6'
		}
		const color = levelColors[entry.level] || '#6366f1'

		return {
			time,
			level: entry.level,
			category: entry.category,
			message: entry.message,
			color,
			data: entry.data
		}
	}

	async function getConfig() {
		const config = {}
		for (const key in DEFAULTS) {
			config[key] = await GM_getValue(key, DEFAULTS[key])
		}
		return config
	}
	async function setConfig(key, value) {
		await GM_setValue(key, value)
	}
	function downloadFile(filename, content, mimeType) {
		const blob = new Blob([content], {type: mimeType})
		const url = URL.createObjectURL(blob)
		const a = document.createElement('a')
		a.href = url
		a.download = filename
		document.body.appendChild(a)
		a.click()
		document.body.removeChild(a)
		URL.revokeObjectURL(url)
	}

	// Helper function to extract and sanitize script name for file system compatibility
	function getScriptName() {
		// Extract script name from userscript metadata
		const scriptName = 'WTR LAB Novel Image Generator'
		// Replace spaces and special characters with underscores for file system compatibility
		return scriptName.replace(/[^\w\s-]/g, '').replace(/\s+/g, '_')
	}

	async function exportConfig() {
		const config = await getConfig()
		const configJson = JSON.stringify(config, null, 2)
		const date = new Date().toISOString().split('T')[0]
		const scriptName = getScriptName()
		downloadFile(`${scriptName}_config_${date}.json`, configJson, 'application/json')
		alert('Configuration exported successfully as a JSON file.')
	}

	async function importConfig(jsonString) {
		try {
			const importedConfig = JSON.parse(jsonString.trim())
			let importedCount = 0
			let errorCount = 0

			for (const key in importedConfig) {
				if (DEFAULTS.hasOwnProperty(key)) {
					await GM_setValue(key, importedConfig[key])
					importedCount++
				} else {
					console.warn(`[NIG] Skipping unknown configuration key: ${key}`)
					errorCount++
				}
			}

			if (importedCount > 0) {
				alert(`Configuration imported successfully! ${importedCount} settings updated. ${errorCount} unknown settings skipped.`)
				// Reload the form to reflect new settings and save them to ensure consistency
				await populateConfigForm()
				await saveConfig()
			} else {
				alert('Import failed: No valid configuration keys found in the provided JSON.')
			}
		} catch (e) {
			console.error('[NIG] Import failed:', e)
			alert('Import failed: Invalid JSON format. Please ensure the file content is valid JSON.')
		}
	}

	function handleImportFile(event) {
		const file = event.target.files[0]
		if (!file) {
			return
		}

		const reader = new FileReader()
		reader.onload = e => {
			importConfig(e.target.result)
			// Clear the file input after import
			event.target.value = ''
		}
		reader.onerror = () => {
			alert('Error reading file.')
			event.target.value = ''
		}
		reader.readAsText(file)
	}

	async function getHistory() {
		return JSON.parse(await GM_getValue('history', '[]'))
	}
	async function addToHistory(item) {
		const history = await getHistory()
		history.unshift(item)
		if (history.length > 100) history.pop()
		await GM_setValue('history', JSON.stringify(history))
	}

	// --- CACHE HELPERS ---
	const CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000 // 24 hours
	async function getCachedModels() {
		return JSON.parse(await GM_getValue('cachedModels', '{}'))
	}
	async function setCachedModels(provider, models) {
		const cache = await getCachedModels()
		cache[provider] = models
		await GM_setValue('cachedModels', JSON.stringify(cache))
	}
	async function clearCachedModels(provider = null) {
		if (provider) {
			const cache = await getCachedModels()
			delete cache[provider]
			await GM_setValue('cachedModels', JSON.stringify(cache))
			log(`Cleared cached models for ${provider}.`)
		} else {
			// Clear model lists for Pollinations and AI Horde
			await GM_setValue('cachedModels', '{}')

			// Clear model selection for OpenAI Compatible profiles
			const profiles = await GM_getValue('openAICompatProfiles', {})
			for (const url in profiles) {
				profiles[url].model = ''
			}
			await GM_setValue('openAICompatProfiles', profiles)
			await GM_setValue('openAICompatModelManualInput', false)
			await GM_setValue('openAICompatActiveProfileUrl', '')

			log('Cleared all cached models and reset OpenAI Compatible model selections.')
			alert('All cached models have been cleared. They will be re-fetched when you next open the settings.')
		}
	}

	// --- 2.1. PROMPT ENHANCEMENT FUNCTIONS ---

	// Provider Priority Detection Logic with Enhanced Logging
	function shouldUseProviderEnhancement(provider, config) {
		logDebug('ENHANCEMENT', 'Checking provider priority for enhancement', {
			provider,
			config: {
				selectedProvider: config.selectedProvider,
				enhancementEnabled: config.enhancementEnabled,
				pollinationsEnhance: config.pollinationsEnhance,
				enhancementOverrideProvider: config.enhancementOverrideProvider
			}
		})

		const shouldUse = (() => {
			switch (provider) {
				case 'Pollinations':
					const result = config.pollinationsEnhance
					logInfo('ENHANCEMENT', `Provider ${provider} has built-in enhancement: ${result}`, {
						provider,
						hasEnhancement: result,
						reason: result ? 'Provider enhancement enabled' : 'Provider enhancement disabled'
					})
					return result
				case 'AIHorde':
				case 'Google':
				case 'OpenAICompat':
				default:
					logInfo('ENHANCEMENT', `Provider ${provider} does not have built-in enhancement`, {
						provider,
						hasEnhancement: false,
						reason: 'Provider does not support built-in enhancement'
					})
					return false
			}
		})()

		logDebug('ENHANCEMENT', 'Provider priority decision completed', {
			provider,
			shouldUseProviderEnhancement: shouldUse,
			willUseExternalAI: config.enhancementEnabled && config.enhancementApiKey && (!shouldUse || config.enhancementOverrideProvider)
		})

		return shouldUse
	}

	// Enhanced Gemini AI API Integration with Comprehensive Logging
	async function enhancePromptWithGemini(originalPrompt, config) {
		const startTime = Date.now()
		const apiKey = config.enhancementApiKey
		const rawModel = config.enhancementModel
		// Fix: Remove "models/" prefix if present to avoid duplicate in URL
		const model = rawModel.startsWith('models/') ? rawModel.substring(7) : rawModel

		// CRITICAL FIX: Enhanced validation and debugging for model name processing
		logDebug('ENHANCEMENT', 'Model name processing validation', {
			rawModel,
			processedModel: model,
			startsWithModelsPrefix: rawModel.startsWith('models/'),
			expectedLength: rawModel.startsWith('models/') ? 7 : 0,
			characterCount: {
				modelsSlashLength: 'models/'.length,
				rawModelLength: rawModel.length,
				processedModelLength: model.length
			},
			urlWillBe: `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`
		})

		// Additional validation to prevent empty or malformed model names
		if (!model || model.trim().length === 0) {
			const error = new Error('Model name processing resulted in empty or invalid model name')
			logError('ENHANCEMENT', 'Invalid model name after processing', {
				originalRawModel: rawModel,
				processedModel: model,
				error: error.message
			})
			throw error
		}

		const template = config.enhancementTemplate.trim()

		logInfo('ENHANCEMENT', 'Starting prompt enhancement with Gemini AI', {
			originalPrompt: originalPrompt.substring(0, 100) + (originalPrompt.length > 100 ? '...' : ''),
			originalLength: originalPrompt.length,
			rawModel,
			processedModel: model,
			apiKeyPresent: !!apiKey,
			apiKeyPrefix: apiKey ? `${apiKey.substring(0, 4)}...${apiKey.substring(apiKey.length - 4)}` : 'none',
			hasCustomTemplate: !!template,
			templateLength: template.length
		})

		if (!apiKey) {
			const error = new Error('Gemini API key is required for prompt enhancement.')
			logError('ENHANCEMENT', 'Enhancement failed - no API key', {
				error: error.message,
				duration: Date.now() - startTime
			})
			throw error
		}

		let enhancementPrompt
		if (template) {
			enhancementPrompt = `${template}\n\nOriginal prompt: "${originalPrompt}"\n\nEnhanced prompt:`
			logDebug('ENHANCEMENT', 'Using custom enhancement template', {
				template,
				enhancedPromptPreview: enhancementPrompt.substring(0, 100) + '...'
			})
		} else {
			enhancementPrompt = `Convert this text into a focused visual description for image generation. Extract key visual elements (characters, setting, mood, style) and describe them as a direct prompt without narrative elements, dialogue, or markdown formatting. Keep it concise and focused on what can be visually rendered. End with quality boosters like "highly detailed, sharp focus, 8K resolution, masterpiece. Generated Prompt Structure: Start with core subjects, layer in scene/mood, then style/technicals:\n\n"${originalPrompt}"\n\nEnhanced version:`
			logDebug('ENHANCEMENT', 'Using default enhancement template')
		}

		const requestData = {
			contents: [
				{
					parts: [
						{
							text: enhancementPrompt
						}
					]
				}
			],
			generationConfig: {
				temperature: 0.7,
				topK: 40,
				topP: 0.95,
				maxOutputTokens: 65536
			}
		}

		const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`

		// CRITICAL FIX: Validate URL formation before making request
		if (model.includes('models/') || apiUrl.includes('models/models/')) {
			const error = new Error('URL validation failed: Duplicate "models/" detected in API URL')
			logError('ENHANCEMENT', 'URL validation failed - duplicate models prefix', {
				processedModel: model,
				apiUrl: apiUrl,
				error: error.message
			})
			throw error
		}

		logDebug('ENHANCEMENT', 'Sending request to Gemini API', {
			url: apiUrl,
			generationConfig: requestData.generationConfig,
			promptLength: enhancementPrompt.length
		})

		return new Promise((resolve, reject) => {
			let isResolved = false

			const timeout = setTimeout(() => {
				if (isResolved) return // Don't reject if already resolved

				const error = new Error('Enhancement request timed out after 45 seconds.')
				logError('ENHANCEMENT', 'Enhancement request timeout', {
					error: error.message,
					duration: Date.now() - startTime,
					originalPrompt: originalPrompt.substring(0, 100)
				})
				reject(error)
			}, 45000)

			GM_xmlhttpRequest({
				method: 'POST',
				url: `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,
				headers: {
					'Content-Type': 'application/json'
				},
				data: JSON.stringify(requestData),
				onload: response => {
					clearTimeout(timeout)
					if (isResolved) return // Don't process if already resolved/rejected

					isResolved = true
					const duration = Date.now() - startTime

					// CRITICAL FIX: Add defensive programming for response.responseText
					const responseText = response.responseText
					const responseLength = responseText ? responseText.length : 0

					logDebug('ENHANCEMENT', 'Received response from Gemini API', {
						status: response.status,
						statusText: response.statusText,
						responseLength: responseLength,
						hasResponseText: !!responseText,
						duration
					})

					// CRITICAL FIX: Check if responseText exists before parsing
					if (!responseText) {
						const errorMessage = 'Empty response received from Gemini API'
						logError('ENHANCEMENT', 'Empty response from Gemini API', {
							error: errorMessage,
							status: response.status,
							statusText: response.statusText,
							duration
						})
						reject(new Error(errorMessage))
						return
					}

					try {
						const data = JSON.parse(response.responseText)

						if (data.error) {
							const errorMessage = data.error.message || 'Gemini API error'
							logError('ENHANCEMENT', 'Gemini API returned error', {
								error: errorMessage,
								errorDetails: data.error,
								status: response.status,
								duration
							})
							reject(new Error(errorMessage))
							return
						}

						if (data.candidates && data.candidates.length > 0) {
							logDebug('ENHANCEMENT', 'Analyzing response structure', {
								candidatesCount: data.candidates.length,
								firstCandidate: data.candidates[0],
								hasContent: !!data.candidates[0].content,
								contentKeys: data.candidates[0].content ? Object.keys(data.candidates[0].content) : [],
								hasParts: !!(data.candidates[0].content && data.candidates[0].content.parts),
								finishReason: data.candidates[0].finishReason,
								partsLength: data.candidates[0].content && data.candidates[0].content.parts ? data.candidates[0].content.parts.length : 0
							})

							// Handle MAX_TOKENS response specifically
							if (data.candidates[0].finishReason === 'MAX_TOKENS') {
								const error = new Error('Enhancement response was truncated due to token limit. Try with a shorter prompt or simpler template.')
								logError('ENHANCEMENT', 'Response truncated - MAX_TOKENS', {
									error: error.message,
									finishReason: data.candidates[0].finishReason,
									promptLength: originalPrompt.length,
									templateLength: template.length,
									totalInputTokens: data.usageMetadata ? data.usageMetadata.promptTokenCount : 'unknown'
								})
								reject(error)
								return
							}

							if (!data.candidates[0].content || !data.candidates[0].content.parts || data.candidates[0].content.parts.length === 0) {
								const error = new Error('Invalid response structure from Gemini API - missing content parts')
								logError('ENHANCEMENT', 'Invalid response structure', {
									error: error.message,
									responseStructure: data.candidates[0],
									hasContent: !!data.candidates[0].content,
									contentKeys: data.candidates[0].content ? Object.keys(data.candidates[0].content) : [],
									finishReason: data.candidates[0].finishReason
								})
								reject(error)
								return
							}

							const enhancedText = data.candidates[0].content.parts[0].text.trim()
							const cleanedText = enhancedText.replace(/^["']|["']$/g, '')

							logInfo('ENHANCEMENT', 'Prompt enhancement completed successfully', {
								duration,
								originalLength: originalPrompt.length,
								enhancedLength: cleanedText.length,
								lengthChange: cleanedText.length - originalPrompt.length,
								apiResponseCandidates: data.candidates.length,
								enhancedPreview: cleanedText.substring(0, 100) + (cleanedText.length > 100 ? '...' : '')
							})

							resolve(cleanedText)
						} else {
							const error = new Error('No enhancement result received from Gemini API')
							logError('ENHANCEMENT', 'No enhancement result in API response', {
								error: error.message,
								apiResponse: data,
								duration
							})
							reject(error)
						}
					} catch (e) {
						const errorMessage = `Enhancement failed: ${e.message}`
						logError('ENHANCEMENT', 'Enhancement processing error', {
							error: errorMessage,
							originalError: e,
							responseText: responseText ? responseText.substring(0, 200) : 'No response text',
							duration
						})
						reject(new Error(errorMessage))
					}
				},
				onerror: error => {
					clearTimeout(timeout)
					if (isResolved) return // Don't process if already resolved/rejected

					isResolved = true
					const duration = Date.now() - startTime
					const errorMessage = `Network error during enhancement: ${JSON.stringify(error)}`
					logError('ENHANCEMENT', 'Network error during enhancement', {
						error: errorMessage,
						networkError: error,
						duration: duration,
						originalPrompt: originalPrompt.substring(0, 100)
					})
					reject(new Error(errorMessage))
				}
			})
		})
	}

	// Enhanced UI Management Functions with Logging
	function updateEnhancementUI(provider, config) {
		const startTime = Date.now()
		const enhancementEnabled = document.getElementById('nig-enhancement-enabled')
		const enhancementSettings = document.getElementById('nig-enhancement-settings')
		const providerPriorityInfo = document.getElementById('nig-provider-priority-info')
		const enhancementStatus = document.getElementById('nig-enhancement-status')
		const statusText = document.getElementById('nig-status-text')
		const statusIndicator = document.getElementById('nig-status-indicator')
		const priorityMessage = document.getElementById('nig-priority-message')

		const providerHasEnhancement = shouldUseProviderEnhancement(provider, config)
		const hasApiKey = config.enhancementApiKey.trim().length > 0

		logDebug('UI', 'Updating enhancement UI state', {
			provider,
			enhancementEnabled: enhancementEnabled.checked,
			providerHasEnhancement,
			hasApiKey,
			overrideProvider: config.enhancementOverrideProvider,
			uiUpdateDuration: Date.now() - startTime
		})

		if (enhancementEnabled.checked && providerHasEnhancement && !config.enhancementOverrideProvider) {
			// Show provider priority info
			providerPriorityInfo.style.display = 'block'
			enhancementSettings.classList.add('disabled')
			priorityMessage.textContent = `${provider} AI enhancement is enabled and will be used instead of external AI enhancement.`

			// Update status
			enhancementStatus.className = 'nig-enhancement-status provider-priority'
			statusText.textContent = 'Provider Enhancement Active'
			statusIndicator.style.background = 'var(--nig-color-accent-warning)'

			logInfo('UI', 'Enhanced UI updated - Provider Priority mode', {
				provider,
				status: 'provider-priority',
				message: priorityMessage.textContent,
				settingsVisible: enhancementSettings.classList.contains('disabled')
			})
		} else {
			// Show enhancement settings
			providerPriorityInfo.style.display = 'none'
			enhancementSettings.classList.remove('disabled')

			if (enhancementEnabled.checked) {
				if (hasApiKey) {
					enhancementStatus.className = 'nig-enhancement-status external-ai'
					statusText.textContent = 'External AI Active'
					statusIndicator.style.background = 'var(--nig-color-accent-primary)'

					logInfo('UI', 'Enhanced UI updated - External AI mode', {
						provider,
						status: 'external-ai',
						hasApiKey: true,
						settingsVisible: true
					})
				} else {
					enhancementStatus.className = 'nig-enhancement-status disabled'
					statusText.textContent = 'API Key Required'
					statusIndicator.style.background = 'var(--nig-color-text-muted)'

					logInfo('UI', 'Enhanced UI updated - API Key Required mode', {
						provider,
						status: 'api-key-required',
						hasApiKey: false,
						settingsVisible: true
					})
				}
			} else {
				enhancementStatus.className = 'nig-enhancement-status disabled'
				statusText.textContent = 'Enhancement Disabled'
				statusIndicator.style.background = 'var(--nig-color-text-muted)'

				logInfo('UI', 'Enhanced UI updated - Disabled mode', {
					provider,
					status: 'disabled',
					enhancementEnabled: false,
					settingsVisible: true
				})
			}
		}
	}

	function toggleEnhancementSettings(enabled) {
		const enhancementSettings = document.getElementById('nig-enhancement-settings')
		const enhancementPreview = document.getElementById('nig-enhancement-preview')
		const apiKeyInput = document.getElementById('nig-gemini-api-key')
		const hasApiKey = apiKeyInput ? apiKeyInput.value.trim().length > 0 : false

		logDebug('UI', 'Toggling enhancement settings', {
			enabled,
			hasApiKey,
			willShowPreview: enabled && hasApiKey
		})

		if (enabled) {
			enhancementSettings.classList.remove('disabled')
			if (hasApiKey) {
				enhancementPreview.style.display = 'block'
				logDebug('UI', 'Enhancement preview enabled', {hasApiKey: true})
			} else {
				logDebug('UI', 'Enhancement preview disabled', {hasApiKey: false})
			}
		} else {
			enhancementSettings.classList.add('disabled')
			enhancementPreview.style.display = 'none'
			logDebug('UI', 'Enhancement settings disabled')
		}
	}

	async function testEnhancement(prompt, config) {
		const startTime = Date.now()
		logInfo('ENHANCEMENT', 'Starting enhancement test', {
			testPrompt: prompt,
			hasApiKey: !!config.enhancementApiKey,
			apiKeyPrefix: config.enhancementApiKey ? `${config.enhancementApiKey.substring(0, 4)}...${config.enhancementApiKey.substring(config.enhancementApiKey.length - 4)}` : 'none'
		})

		if (!config.enhancementApiKey) {
			const error = new Error('API key required for testing enhancement')
			logError('ENHANCEMENT', 'Enhancement test failed - no API key', {
				error: error.message,
				duration: Date.now() - startTime
			})
			throw error
		}

		try {
			logDebug('ENHANCEMENT', 'Calling enhancePromptWithGemini for test')
			const enhancedPrompt = await enhancePromptWithGemini(prompt, config)
			const duration = Date.now() - startTime

			const result = {
				original: prompt,
				enhanced: enhancedPrompt
			}

			logInfo('ENHANCEMENT', 'Enhancement test completed successfully', {
				duration,
				originalLength: prompt.length,
				enhancedLength: enhancedPrompt.length,
				lengthChange: enhancedPrompt.length - prompt.length,
				improvementRatio: (enhancedPrompt.length / prompt.length).toFixed(2)
			})

			return result
		} catch (error) {
			const duration = Date.now() - startTime
			const errorMessage = `Enhancement test failed: ${error.message}`
			logError('ENHANCEMENT', 'Enhancement test failed', {
				error: errorMessage,
				originalError: error,
				duration,
				testPrompt: prompt.substring(0, 100)
			})
			throw new Error(errorMessage)
		}
	}

	// --- 3. UI ELEMENTS ---
	let generateBtn, configPanel, imageViewer, googleApiPrompt, pollinationsAuthPrompt, statusWidget, errorModal
	let currentSelection = ''

	function createUI() {
		const styleSheet = document.createElement('style')
		styleSheet.innerText = styles
		document.head.appendChild(styleSheet)
		// Add Google Material Symbols font
		const materialSymbolsLink = document.createElement('link')
		materialSymbolsLink.rel = 'stylesheet'
		materialSymbolsLink.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0'
		document.head.appendChild(materialSymbolsLink)

		generateBtn = document.createElement('button')
		generateBtn.className = 'nig-button'
		generateBtn.innerHTML = '🎨 Generate Image'
		generateBtn.addEventListener('click', onGenerateClick)
		document.body.appendChild(generateBtn)
		statusWidget = document.createElement('div')
		statusWidget.id = 'nig-status-widget'
		statusWidget.className = 'nig-status-widget'
		statusWidget.innerHTML = `<div class="nig-status-icon"></div><span class="nig-status-text"></span>`
		document.body.appendChild(statusWidget)
	}

	function createConfigPanel() {
		if (document.getElementById('nig-config-panel')) return
		const googleSettingsHTML = `
            <div class="nig-form-group"><label for="nig-google-api-key">Gemini API Key</label><input type="password" id="nig-google-api-key"></div>
            <div class="nig-form-group"><label for="nig-model">Imagen Model</label><select id="nig-model"><option value="imagen-4.0-generate-001">Imagen 4.0 Standard</option><option value="imagen-4.0-ultra-generate-001">Imagen 4.0 Ultra</option><option value="imagen-4.0-fast-generate-001">Imagen 4.0 Fast</option><option value="imagen-3.0-generate-002">Imagen 3.0 Standard</option></select></div>
            <div class="nig-form-group"><label for="nig-num-images">Number of Images (1-4)</label><input type="number" id="nig-num-images" min="1" max="4" step="1"></div>
            <div class="nig-form-group"><label for="nig-image-size">Image Size</label><select id="nig-image-size"><option value="1024">1K</option><option value="2048">2K</option></select></div>
            <div class="nig-form-group"><label for="nig-aspect-ratio">Aspect Ratio</label><select id="nig-aspect-ratio"><option value="1:1">1:1</option><option value="3:4">3:4</option><option value="4:3">4:3</option><option value="9:16">9:16</option><option value="16:9">16:9</option></select></div>
            <div class="nig-form-group"><label for="nig-person-gen">Person Generation</label><select id="nig-person-gen"><option value="dont_allow">Don't Allow</option><option value="allow_adult">Allow Adults</option><option value="allow_all">Allow All</option></select></div>`
		const pollinationsSettingsHTML = `
            <div class="nig-form-group"><label for="nig-pollinations-model">Model</label><select id="nig-pollinations-model"><option>Loading models...</option></select></div>
            <div class="nig-form-group-inline">
                <div><label for="nig-pollinations-width">Width</label><input type="number" id="nig-pollinations-width" min="64" max="2048" step="64"></div>
                <div><label for="nig-pollinations-height">Height</label><input type="number" id="nig-pollinations-height" min="64" max="2048" step="64"></div>
            </div>
            <div class="nig-form-group"><label for="nig-pollinations-seed">Seed (optional)</label><input type="text" id="nig-pollinations-seed" placeholder="Leave blank for random"></div>
            <div class="nig-form-group"><label>Options</label>
                <div class="nig-checkbox-group">
                    <label><input type="checkbox" id="nig-pollinations-enhance">Enhance Prompt</label>
                    <label><input type="checkbox" id="nig-pollinations-safe">Safe Mode (NSFW Filter)</label>
                    <label><input type="checkbox" id="nig-pollinations-nologo">No Logo (Registered Users)</label>
                    <label><input type="checkbox" id="nig-pollinations-private">Private (Won't appear in feed)</label>
                </div>
            </div>
            <div class="nig-form-group">
                <label for="nig-pollinations-token">API Token (Optional)</label>
                <input type="password" id="nig-pollinations-token" placeholder="Enter token for premium models">
                <small class="nig-hint">Get a token from <a href="https://auth.pollinations.ai" target="_blank" class="nig-api-prompt-link">auth.pollinations.ai</a> for higher rate limits and access to restricted models.</small>
            </div>
        `
		const openAICompatSettingsHTML = `
            <div class="nig-form-group">
                <label for="nig-openai-compat-profile-select">Saved Profiles</label>
                <div class="nig-form-group-inline">
                    <select id="nig-openai-compat-profile-select"></select>
                    <button id="nig-openai-compat-delete-profile" class="nig-delete-btn">Delete</button>
                </div>
            </div>
            <div class="nig-form-group">
                <label for="nig-openai-compat-base-url">Base URL</label>
                <input type="text" id="nig-openai-compat-base-url" placeholder="e.g., https://api.example.com/v1">
                <small class="nig-hint">For a list of free public providers, check out the <a href="https://github.com/zukixa/cool-ai-stuff" target="_blank" class="nig-api-prompt-link">cool-ai-stuff</a> repository.</small>
            </div>
            <div class="nig-form-group">
                <label for="nig-openai-compat-api-key">API Key</label>
                <input type="password" id="nig-openai-compat-api-key">
            </div>
            <div class="nig-form-group">
                <label for="nig-openai-compat-model">Model</label>
                <div id="nig-openai-model-container-select">
                    <div class="nig-form-group-inline">
                        <select id="nig-openai-compat-model" style="width: 100%;"><option>Enter URL/Key and fetch...</option></select>
                        <button id="nig-openai-compat-fetch-models" class="nig-fetch-models-btn">Fetch</button>
                    </div>
                    <small class="nig-hint">If fetching fails or your model isn't listed, <a href="#" id="nig-openai-compat-switch-to-manual" class="nig-api-prompt-link">switch to manual input</a>.</small>
                </div>
                <div id="nig-openai-model-container-manual" style="display: none;">
                    <input type="text" id="nig-openai-compat-model-manual" placeholder="e.g., dall-e-3">
                    <small class="nig-hint">Manually enter the model name. <a href="#" id="nig-openai-compat-switch-to-select" class="nig-api-prompt-link">Switch back to fetched list</a>.</small>
                </div>
            </div>
        `
		configPanel = document.createElement('div')
		configPanel.id = 'nig-config-panel'
		configPanel.className = 'nig-modal-overlay'
		configPanel.innerHTML = `
            <div class="nig-modal-content">
                <span class="nig-close-btn">&times;</span><h2>Image Generator Configuration</h2>
                <div class="nig-tabs">
                    <div class="nig-tab active" data-tab="config">Configuration</div>
                    <div class="nig-tab" data-tab="styling">Prompt Styling</div>
                    <div class="nig-tab" data-tab="history">History</div>
                    <div class="nig-tab" data-tab="utilities">Utilities</div>
                </div>
                <div id="nig-config-tab" class="nig-tab-content active">
                    <div class="nig-config-grid">
                        <div class="nig-config-section">
                            <div class="nig-form-group">
                                <label for="nig-provider">Image Provider</label>
                                <select id="nig-provider">
                                    <option value="Pollinations">🌱 Pollinations.ai (Free, Simple)</option>
                                    <option value="AIHorde">🤖 AI Horde (Free, Advanced)</option>
                                    <option value="OpenAICompat">🔌 OpenAI Compatible (Custom)</option>
                                    <option value="Google">🖼️ Google Imagen (Requires Billed Account)</option>
                                </select>
                            </div>
                        </div>

                        <div class="nig-provider-container">
                            <div id="nig-provider-Pollinations" class="nig-provider-settings">
                                <div class="nig-provider-header">
                                    <h3>🌱 Pollinations.ai Settings</h3>
                                    <p>Fast, simple image generation with advanced model options</p>
                                </div>
                                ${pollinationsSettingsHTML}
                            </div>

                            <div id="nig-provider-AIHorde" class="nig-provider-settings">
                                <div class="nig-provider-header">
                                    <h3>🤖 AI Horde Settings</h3>
                                    <p>Community-powered generation with extensive customization</p>
                                </div>
                                <div class="nig-form-group">
                                    <label for="nig-horde-api-key">AI Horde API Key</label>
                                    <input type="password" id="nig-horde-api-key" placeholder="Defaults to '0000000000'">
                                    <small>Use anonymous key or get your own from <a href="https://aihorde.net/" target="_blank" class="nig-api-prompt-link">AI Horde</a> for higher priority.</small>
                                </div>
                                <div class="nig-provider-controls">
                                    <div class="nig-form-group">
                                        <label for="nig-horde-model">Model</label>
                                        <select id="nig-horde-model"><option>Loading models...</option></select>
                                    </div>
                                    <div class="nig-form-group">
                                        <label for="nig-horde-sampler">Sampler</label>
                                        <select id="nig-horde-sampler">
                                            <option value="k_dpmpp_2m">DPM++ 2M</option>
                                            <option value="k_euler_a">Euler A</option>
                                            <option value="k_euler">Euler</option>
                                            <option value="k_lms">LMS</option>
                                            <option value="k_heun">Heun</option>
                                            <option value="k_dpm_2">DPM 2</option>
                                            <option value="k_dpm_2_a">DPM 2 A</option>
                                            <option value="k_dpmpp_2s_a">DPM++ 2S A</option>
                                            <option value="k_dpmpp_sde">DPM++ SDE</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="nig-form-grid">
                                    <div class="nig-form-group">
                                        <label for="nig-horde-steps">Steps</label>
                                        <input type="number" id="nig-horde-steps" min="10" max="50" step="1">
                                        <small class="nig-hint">More steps = more detail, but slower.</small>
                                    </div>
                                    <div class="nig-form-group">
                                        <label for="nig-horde-cfg">CFG Scale</label>
                                        <input type="number" id="nig-horde-cfg" min="1" max="20" step="0.5">
                                        <small class="nig-hint">How strictly to follow the prompt.</small>
                                    </div>
                                </div>
                                <div class="nig-form-grid">
                                    <div class="nig-form-group">
                                        <label for="nig-horde-width">Width</label>
                                        <input type="number" id="nig-horde-width" min="64" max="2048" step="64">
                                    </div>
                                    <div class="nig-form-group">
                                        <label for="nig-horde-height">Height</label>
                                        <input type="number" id="nig-horde-height" min="64" max="2048" step="64">
                                    </div>
                                </div>
                                <div class="nig-form-group">
                                    <label for="nig-horde-seed">Seed (optional)</label>
                                    <input type="text" id="nig-horde-seed" placeholder="Leave blank for random">
                                </div>
                                <div class="nig-form-group">
                                    <label>Post-Processing</label>
                                    <small class="nig-hint">Improves faces. Use only if generating people.</small>
                                    <div class="nig-checkbox-group">
                                        <label><input type="checkbox" name="nig-horde-post" value="GFPGAN">GFPGAN</label>
                                        <label><input type="checkbox" name="nig-horde-post" value="CodeFormers">CodeFormers</label>
                                    </div>
                                </div>
                            </div>

                            <div id="nig-provider-Google" class="nig-provider-settings">
                                <div class="nig-provider-header">
                                    <h3>🖼️ Google Imagen Settings</h3>
                                    <p>High-quality generation powered by Google's advanced AI</p>
                                </div>
                                ${googleSettingsHTML}
                            </div>

                            <div id="nig-provider-OpenAICompat" class="nig-provider-settings">
                                <div class="nig-provider-header">
                                    <h3>🔌 OpenAI Compatible Settings</h3>
                                    <p>Connect to any OpenAI-compatible API endpoint</p>
                                </div>
                                ${openAICompatSettingsHTML}
                            </div>
                        </div>
                    </div>
                </div>

                <div id="nig-styling-tab" class="nig-tab-content">
                    <div class="nig-styling-container">
                        <div class="nig-styling-intro">
                            <p>Select a style to automatically add it to the beginning of every prompt. This helps maintain a consistent look across all providers.</p>
                        </div>

                        <div class="nig-style-grid">
                            <div class="nig-style-section">
                                <div class="nig-form-group">
                                    <label for="nig-main-style">Main Style</label>
                                    <select id="nig-main-style"></select>
                                    <small id="nig-main-style-desc" class="nig-hint"></small>
                                </div>
                                <div class="nig-form-group">
                                    <label for="nig-sub-style">Sub-Style</label>
                                    <select id="nig-sub-style"></select>
                                    <small id="nig-sub-style-desc" class="nig-hint"></small>
                                </div>
                            </div>

                            <div class="nig-style-section">
                                <div class="nig-section-header">
                                    <h4>
                                        <span class="material-symbols-outlined">auto_awesome</span>
                                        AI Prompt Enhancement
                                        <span class="nig-enhancement-status" id="nig-enhancement-status">
                                            <span class="nig-status-indicator" id="nig-status-indicator"></span>
                                            <span id="nig-status-text">Enhancement Disabled</span>
                                        </span>
                                    </h4>
                                </div>
                                
                                <div class="nig-enhancement-content">
                                    <!-- Enable/Disable Toggle -->
                                    <div class="nig-form-group">
                                        <div class="nig-checkbox-group">
                                            <label><input type="checkbox" id="nig-enhancement-enabled">Enable AI Prompt Enhancement</label>
                                        </div>
                                        <small class="nig-hint">Uses AI to automatically enhance prompts for better results. Provider enhancement takes priority when available.</small>
                                    </div>
                                    
                                    <!-- Provider Priority Status -->
                                    <div class="nig-provider-priority-info" id="nig-provider-priority-info" style="display: none;">
                                        <div class="nig-priority-header">
                                            <span class="material-symbols-outlined">priority_high</span>
                                            Provider Enhancement Active
                                        </div>
                                        <p id="nig-priority-message">Pollinations AI enhancement is enabled and will be used instead of external AI enhancement.</p>
                                        <button class="nig-override-btn" id="nig-override-provider">Force Use External AI</button>
                                    </div>
                                    
                                    <!-- Enhancement Settings -->
                                    <div class="nig-enhancement-settings disabled" id="nig-enhancement-settings">
                                        <!-- API Configuration -->
                                        <div class="nig-form-group">
                                            <label for="nig-gemini-api-key">Gemini API Key</label>
                                            <input type="password" id="nig-gemini-api-key" placeholder="Enter your Google Gemini API key">
                                            <small class="nig-hint">Get a free API key from <a href="https://aistudio.google.com/api-keys" target="_blank" class="nig-api-prompt-link">Google AI Studio</a></small>
                                        </div>
                                        
                                        <!-- Model Selection -->
                                        <div class="nig-form-group">
                                            <label for="nig-enhancement-model">Enhancement Model</label>
                                            <select id="nig-enhancement-model">
                                                <option value="models/gemini-2.5-pro">Gemini 2.5 Pro (High Quality)</option>
                                                <option value="models/gemini-flash-latest">Gemini Flash Latest (Fast)</option>
                                                <option value="models/gemini-flash-lite-latest">Gemini Flash Lite (Ultra Fast)</option>
                                                <option value="models/gemini-2.5-flash">Gemini 2.5 Flash (Balanced)</option>
                                                <option value="models/gemini-2.5-flash-lite">Gemini 2.5 Flash Lite (Efficient)</option>
                                            </select>
                                            <small class="nig-hint">Choose model based on your needs: quality vs speed</small>
                                        </div>
                                        
                                        <!-- Custom Prompt Template -->
                                        <div class="nig-form-group">
                                            <label for="nig-enhancement-template">Custom Enhancement Template</label>
                                            <textarea id="nig-enhancement-template" rows="3" placeholder="Enter custom enhancement instructions..."></textarea>
                                            <div class="nig-form-group-inline">
                                                <button class="nig-template-btn" id="nig-template-default">Reset to Default</button>
                                                <button class="nig-template-btn" id="nig-template-example">Load Example</button>
                                            </div>
                                            <small class="nig-hint">Customize how the AI enhances prompts. Leave empty for intelligent enhancement.</small>
                                        </div>
                                        
                                        <!-- Preview Interface -->
                                        <div class="nig-enhancement-preview" id="nig-enhancement-preview" style="display: none;">
                                            <div class="nig-preview-container">
                                                <div class="nig-preview-section">
                                                    <h5>Original Prompt</h5>
                                                    <div class="nig-prompt-display" id="nig-original-prompt"></div>
                                                </div>
                                                <div class="nig-preview-arrow">
                                                    <span class="material-symbols-outlined">arrow_forward</span>
                                                </div>
                                                <div class="nig-preview-section">
                                                    <h5>Enhanced Prompt</h5>
                                                    <div class="nig-prompt-display" id="nig-enhanced-prompt"></div>
                                                </div>
                                            </div>
                                            <button class="nig-test-enhancement-btn" id="nig-test-enhancement">
                                                <span class="material-symbols-outlined">auto_awesome</span>
                                                Test Enhancement
                                            </button>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="nig-style-section">
                                <div class="nig-section-header">
                                    <h4>Custom Style</h4>
                                </div>
                                <div class="nig-form-group">
                                    <div class="nig-checkbox-group">
                                        <label><input type="checkbox" id="nig-custom-style-enable">Enable Custom Style</label>
                                    </div>
                                    <small class="nig-hint">Overrides the Main/Sub-style dropdowns with your own text.</small>
                                    <textarea id="nig-custom-style-text" placeholder="e.g., In the style of Van Gogh, oil painting, ..."></textarea>
                                </div>
                            </div>

                            <div class="nig-style-section">
                                <div class="nig-section-header">
                                    <h4>Negative Prompting (Global)</h4>
                                </div>
                                <div class="nig-form-group">
                                    <div class="nig-checkbox-group">
                                        <label><input type="checkbox" id="nig-enable-neg-prompt">Enable Negative Prompting</label>
                                    </div>
                                    <small class="nig-hint">This negative prompt will be applied to all providers when enabled.</small>
                                    <textarea id="nig-global-neg-prompt" placeholder="e.g., ugly, blurry, deformed, disfigured, poor details, bad anatomy, low quality"></textarea>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div id="nig-history-tab" class="nig-tab-content">
                    <div class="nig-history-container">
                        <div class="nig-history-cleanup">
                            <div class="nig-cleanup-info">
                                <h4>History Management</h4>
                                <p>Clean up old history entries to free up space and improve performance.</p>
                            </div>
                            <div class="nig-cleanup-controls">
                                <label>Delete history older than</label>
                                <input type="number" id="nig-history-clean-days" min="1" max="365" value="30">
                                <label>days</label>
                                <button id="nig-history-clean-btn" class="nig-history-cleanup-btn">
                                    <span class="material-symbols-outlined">cleaning_services</span>
                                    Clean
                                </button>
                            </div>
                        </div>
                        <ul id="nig-history-list" class="nig-history-list"></ul>
                    </div>
                </div>
                <div id="nig-utilities-tab" class="nig-tab-content">
                    <div class="nig-utilities-grid">
                        <div class="nig-utility-card">
                            <h4>Import/Export Settings</h4>
                            <p>Backup and restore your configuration settings for seamless setup across different sessions or devices.</p>
                            <div class="nig-form-group">
                                <button id="nig-export-btn" class="nig-save-btn" style="background-color: var(--nig-color-accent-primary);">
                                    <span class="material-symbols-outlined">download</span>
                                    Download Configuration
                                </button>
                                <small class="nig-hint">Downloads the current configuration as a JSON file.</small>
                            </div>
                            <div class="nig-form-group">
                                <label for="nig-import-file">Import Configuration</label>
                                <input type="file" id="nig-import-file" accept=".json" style="border: 2px dashed var(--nig-color-border); background: var(--nig-color-bg-primary);">
                                <small class=" nig-hint">Uploading a JSON file will overwrite all current settings.</small>
                            </div>
                        </div>

                        <div class="nig-utility-card">
                            <h4>Cache Management</h4>
                            <p>Clear cached model lists and force fresh data fetching for accurate, up-to-date information.</p>
                            <button id="nig-clear-cache-btn" class="nig-save-btn" style="background-color: var(--nig-color-accent-error);">
                                <span class="material-symbols-outlined">clear_all</span>
                                Clear Cached Models
                            </button>
                            <small class="nig-hint">Removes all cached model lists forcing a fresh fetch.</small>
                        </div>

                        <div class="nig-utility-card">
                            <div class="nig-card-header">
                                <div class="nig-card-title">
                                    <h4>Debug Console & Logs</h4>
                                    <p>Enable detailed console logging and view enhancement operation logs to troubleshoot issues and monitor system behavior during development.</p>
                                </div>
                            </div>
                            
                            <div class="nig-card-actions">
                                <button id="nig-toggle-logging-btn" class="nig-btn-primary">
                                    <span class="material-symbols-outlined">bug_report</span>
                                    Toggle Console Logging & Enhancement Logs
                                </button>
                            </div>
                            
                            <div class="nig-card-secondary-actions">
                                <button id="nig-view-enhancement-logs-btn" class="nig-btn-secondary">
                                    <span class="material-symbols-outlined">list</span>
                                    View Enhancement Logs
                                </button>
                                <button id="nig-clear-enhancement-logs-btn" class="nig-btn-secondary nig-btn-error">
                                    <span class="material-symbols-outlined">clear_all</span>
                                    Clear Logs
                                </button>
                            </div>
                            
                            <div class="nig-card-footer">
                                <small class="nig-hint">Toggles detailed console logging and provides access to enhancement operation logs.</small>
                            </div>
                        </div>
                   </div>
               </div>
               <div class="nig-button-footer">
                   <button id="nig-save-btn" class="nig-save-btn">Save Configuration</button>
               </div>
           </div>`
		document.body.appendChild(configPanel)
		configPanel.querySelector('.nig-close-btn').addEventListener('click', () => (configPanel.style.display = 'none'))
		configPanel.querySelector('#nig-save-btn').addEventListener('click', saveConfig)
		configPanel.querySelectorAll('.nig-tab').forEach(tab => {
			tab.addEventListener('click', async () => {
				configPanel.querySelectorAll('.nig-tab, .nig-tab-content').forEach(el => el.classList.remove('active'))
				tab.classList.add('active')
				configPanel.querySelector(`#nig-${tab.dataset.tab}-tab`).classList.add('active')
				if (tab.dataset.tab === 'history') {
					await populateHistoryTab()
					configPanel.querySelector('#nig-save-btn').style.display = 'none'
				} else {
					configPanel.querySelector('#nig-save-btn').style.display = 'block'
				}
			})
		})
		configPanel.querySelector('#nig-provider').addEventListener('change', updateVisibleSettings)
		configPanel.querySelector('#nig-openai-compat-fetch-models').addEventListener('click', () => fetchOpenAICompatModels())
		configPanel.querySelector('#nig-openai-compat-profile-select').addEventListener('change', loadSelectedOpenAIProfile)
		configPanel.querySelector('#nig-openai-compat-delete-profile').addEventListener('click', deleteSelectedOpenAIProfile)
		configPanel.querySelector('#nig-export-btn').addEventListener('click', exportConfig)
		configPanel.querySelector('#nig-import-file').addEventListener('change', handleImportFile)
		configPanel.querySelector('#nig-history-clean-btn').addEventListener('click', cleanHistory)
		configPanel.querySelector('#nig-clear-cache-btn').addEventListener('click', () => clearCachedModels())
		configPanel.querySelector('#nig-toggle-logging-btn').addEventListener('click', async () => {
			const currentState = await GM_getValue('loggingEnabled', DEFAULTS.loggingEnabled)
			const newState = !currentState
			await GM_setValue('loggingEnabled', newState)
			await updateLoggingStatus()
			await loadEnhancementLogHistory()
			alert(`Debug Console & Enhancement Logs are now ${newState ? 'ENABLED' : 'DISABLED'}.`)

			logInfo('UI', 'Debug Console and Enhancement Logs toggled', {
				action: newState ? 'enabled' : 'disabled',
				previousState: currentState,
				newState: newState,
				userInteraction: 'toggle-logging-btn',
				functionality: 'console-logging-and-enhancement-logs'
			})
		})

		// Enhancement logs functionality
		configPanel.querySelector('#nig-view-enhancement-logs-btn').addEventListener('click', async () => {
			const logs = await getEnhancementLogHistory()

			if (logs.length === 0) {
				alert('No enhancement logs found. Enhancement logging is disabled or no enhancement operations have been performed yet.')
				logInfo('UI', 'View enhancement logs requested but no logs available', {
					logsCount: logs.length,
					loggingEnabled: loggingEnabled,
					userInteraction: 'view-enhancement-logs-btn'
				})
				return
			}

			const logModal = document.createElement('div')
			logModal.className = 'nig-modal-overlay'
			logModal.innerHTML = `
				<div class="nig-modal-content">
					<span class="nig-close-btn">&times;</span>
					<h2>Enhancement Operation Logs</h2>
					<p>Detailed logs of prompt enhancement operations with timestamps and performance data.</p>
					<div style="max-height: 400px; overflow-y: auto; background: var(--nig-color-bg-tertiary); border-radius: var(--nig-radius-md); padding: var(--nig-space-lg); margin: var(--nig-space-lg) 0;">
						<div id="nig-enhancement-logs-display"></div>
					</div>
					<div style="margin-top: var(--nig-space-xl); padding-top: var(--nig-space-lg); border-top: 1px solid var(--nig-color-border);">
						<button id="nig-export-enhancement-logs-btn" class="nig-save-btn" style="background-color: var(--nig-color-accent-primary); margin-right: var(--nig-space-md);">
							<span class="material-symbols-outlined">download</span>
							Export Logs
						</button>
						<button id="nig-clear-enhancement-logs-btn-modal" class="nig-delete-btn" style="background-color: var(--nig-color-accent-error);">
							<span class="material-symbols-outlined">clear_all</span>
							Clear All Logs
						</button>
					</div>
				</div>
			`
			document.body.appendChild(logModal)

			// Display logs
			const logsDisplay = logModal.querySelector('#nig-enhancement-logs-display')
			logs.slice(0, 50).forEach(log => {
				const formatted = formatLogEntry(log)
				const logEntry = document.createElement('div')
				logEntry.style.cssText = `
					padding: var(--nig-space-sm) 0;
					border-bottom: 1px solid var(--nig-color-border);
					font-family: 'Fira Code', monospace;
					font-size: var(--nig-font-size-xs);
				`
				logEntry.innerHTML = `
					<div style="display: flex; align-items: center; gap: var(--nig-space-sm); margin-bottom: var(--nig-space-xs);">
						<span style="color: ${formatted.color}; font-weight: 600;">[${formatted.level}]</span>
						<span style="color: var(--nig-color-text-muted); font-size: var(--nig-font-size-xs);">${formatted.time}</span>
						<span style="color: var(--nig-color-accent-primary); font-weight: 500;">[${formatted.category}]</span>
					</div>
					<div style="color: var(--nig-color-text-primary); margin-bottom: var(--nig-space-xs);">${formatted.message}</div>
					${formatted.data ? `<pre style="color: var(--nig-color-text-secondary); font-size: var(--nig-font-size-xs); background: var(--nig-color-bg-primary); padding: var(--nig-space-sm); border-radius: var(--nig-radius-sm); margin: 0; overflow-x: auto;">${JSON.stringify(formatted.data, null, 2)}</pre>` : ''}
				`
				logsDisplay.appendChild(logEntry)
			})

			// Event listeners
			logModal.querySelector('.nig-close-btn').addEventListener('click', () => logModal.remove())
			logModal.querySelector('#nig-export-enhancement-logs-btn').addEventListener('click', () => {
				const logsData = JSON.stringify(logs, null, 2)
				const filename = `enhancement_logs_${new Date().toISOString().split('T')[0]}.json`
				downloadFile(filename, logsData, 'application/json')
				logInfo('UI', 'Enhancement logs exported', {
					logsCount: logs.length,
					filename: filename,
					userInteraction: 'export-enhancement-logs-btn'
				})
			})
			logModal.querySelector('#nig-clear-enhancement-logs-btn-modal').addEventListener('click', () => {
				if (confirm('Are you sure you want to clear all enhancement logs? This action cannot be undone.')) {
					clearEnhancementLogs()
					logModal.remove()
					alert('All enhancement logs have been cleared.')
					logInfo('UI', 'Enhancement logs cleared', {
						userInteraction: 'clear-enhancement-logs-btn-modal',
						action: 'clear-all-logs'
					})
				}
			})

			logInfo('UI', 'Enhancement logs viewer opened', {
				logsCount: logs.length,
				displayedCount: Math.min(logs.length, 50),
				userInteraction: 'view-enhancement-logs-btn'
			})
		})

		configPanel.querySelector('#nig-clear-enhancement-logs-btn').addEventListener('click', async () => {
			const logs = await getEnhancementLogHistory()
			if (logs.length === 0) {
				alert('No enhancement logs to clear.')
				return
			}

			if (confirm(`Are you sure you want to clear all ${logs.length} enhancement logs? This action cannot be undone.`)) {
				clearEnhancementLogs()
				alert('All enhancement logs have been cleared.')
				logInfo('UI', 'Enhancement logs cleared', {
					clearedCount: logs.length,
					userInteraction: 'clear-enhancement-logs-btn'
				})
			}
		})
		const customStyleEnable = configPanel.querySelector('#nig-custom-style-enable')
		const customStyleText = configPanel.querySelector('#nig-custom-style-text')
		customStyleEnable.addEventListener('change', () => {
			customStyleText.disabled = !customStyleEnable.checked
		})

		const switchToManual = e => {
			e.preventDefault()
			document.getElementById('nig-openai-model-container-select').style.display = 'none'
			document.getElementById('nig-openai-model-container-manual').style.display = 'block'
		}
		const switchToSelect = e => {
			e.preventDefault()
			document.getElementById('nig-openai-model-container-select').style.display = 'block'
			document.getElementById('nig-openai-model-container-manual').style.display = 'none'
		}
		configPanel.querySelector('#nig-openai-compat-switch-to-manual').addEventListener('click', switchToManual)
		// Enhancement UI Event Listeners
		const enhancementEnabled = configPanel.querySelector('#nig-enhancement-enabled')
		const enhancementSettings = configPanel.querySelector('#nig-enhancement-settings')
		const enhancementPreview = configPanel.querySelector('#nig-enhancement-preview')
		const overrideProviderBtn = configPanel.querySelector('#nig-override-provider')
		const templateDefaultBtn = configPanel.querySelector('#nig-template-default')
		const templateExampleBtn = configPanel.querySelector('#nig-template-example')
		const testEnhancementBtn = configPanel.querySelector('#nig-test-enhancement')
		const geminiApiKeyInput = configPanel.querySelector('#nig-gemini-api-key')

		// Enhanced toggle functionality with logging
		enhancementEnabled.addEventListener('change', async e => {
			const newState = e.target.checked
			const config = await getConfig()
			config.enhancementEnabled = newState
			await setConfig('enhancementEnabled', newState)
			toggleEnhancementSettings(newState)

			// Update UI based on provider and config
			const provider = document.getElementById('nig-provider').value
			updateEnhancementUI(provider, config)

			logInfo('UI', 'Enhancement toggle changed', {
				action: newState ? 'enabled' : 'disabled',
				provider,
				userInteraction: 'enhancement-toggle'
			})
		})

		// Enhanced provider override functionality with logging
		overrideProviderBtn.addEventListener('click', async () => {
			const provider = document.getElementById('nig-provider').value
			await setConfig('enhancementOverrideProvider', true)
			const config = await getConfig()
			updateEnhancementUI(provider, config)

			logInfo('UI', 'Provider override activated', {
				provider,
				action: 'force-use-external-ai',
				userInteraction: 'override-provider-btn',
				reason: 'User chose to use external AI despite provider having enhancement'
			})
		})

		// Enhanced template management with logging
		templateDefaultBtn.addEventListener('click', async () => {
			const defaultTemplate = 'Convert this text into a focused visual description for image generation. Extract key visual elements (characters, setting, mood, style) and describe them as a direct prompt without narrative elements, dialogue, or markdown formatting. Keep it concise and focused on what can be visually rendered. End with quality boosters like "highly detailed, sharp focus, 8K resolution, masterpiece. Generated Prompt Structure: Start with core subjects, layer in scene/mood, then style/technicals:'
			const oldTemplate = configPanel.querySelector('#nig-enhancement-template').value
			configPanel.querySelector('#nig-enhancement-template').value = defaultTemplate
			await setConfig('enhancementTemplate', defaultTemplate)

			logInfo('UI', 'Enhancement template reset to default', {
				action: 'template-default',
				oldTemplateLength: oldTemplate.length,
				newTemplateLength: defaultTemplate.length,
				userInteraction: 'template-default-btn'
			})
		})

		templateExampleBtn.addEventListener('click', async () => {
			const exampleTemplate = 'Extract visual elements from this text and craft a concise image generation prompt as a flowing paragraph. Focus on: characters and their appearances/actions/expressions, setting and environment, lighting/mood/color palette, artistic style/composition/framing. Omit narrative, dialogue, text, or non-visual details. Use vivid, specific descriptors separated by commas or short phrases for clarity. End with quality boosters like "highly detailed, sharp focus, 8K resolution, masterpiece. Generated Prompt Structure: Start with core subjects, layer in scene/mood, then style/technicals:'
			const oldTemplate = configPanel.querySelector('#nig-enhancement-template').value
			configPanel.querySelector('#nig-enhancement-template').value = exampleTemplate
			await setConfig('enhancementTemplate', exampleTemplate)

			logInfo('UI', 'Enhancement template set to example', {
				action: 'template-example',
				oldTemplateLength: oldTemplate.length,
				newTemplateLength: exampleTemplate.length,
				userInteraction: 'template-example-btn'
			})
		})

		// Enhanced API key validation with logging
		geminiApiKeyInput.addEventListener('input', async e => {
			const newValue = e.target.value
			const hasApiKey = newValue.trim().length > 0
			const prevHasApiKey = e.target._prevHasApiKey || false

			e.target._prevHasApiKey = hasApiKey

			if (hasApiKey) {
				enhancementPreview.style.display = 'block'
			} else {
				enhancementPreview.style.display = 'none'
			}

			// Log only when state changes
			if (hasApiKey !== prevHasApiKey) {
				logInfo('UI', 'API key input state changed', {
					action: hasApiKey ? 'api-key-added' : 'api-key-removed',
					hasApiKey,
					apiKeyPrefix: hasApiKey ? `${newValue.trim().substring(0, 4)}...${newValue.trim().substring(newValue.trim().length - 4)}` : 'none',
					previewVisible: enhancementPreview.style.display !== 'none',
					userInteraction: 'api-key-input'
				})
			}
		})

		// Enhanced test enhancement functionality with logging
		testEnhancementBtn.addEventListener('click', async () => {
			const config = await getConfig()
			const testPrompt = 'A mystical warrior standing on a cliff overlooking a magical forest with glowing mushrooms and ethereal mist'
			const startTime = Date.now()

			testEnhancementBtn.disabled = true
			const originalContent = testEnhancementBtn.innerHTML
			testEnhancementBtn.innerHTML = '<span class="material-symbols-outlined">hourglass_empty</span>Testing...'

			logInfo('UI', 'Enhancement test started', {
				testPrompt: testPrompt.substring(0, 100),
				hasApiKey: !!config.enhancementApiKey,
				apiKeyPrefix: config.enhancementApiKey ? `${config.enhancementApiKey.substring(0, 4)}...${config.enhancementApiKey.substring(config.enhancementApiKey.length - 4)}` : 'none',
				userInteraction: 'test-enhancement-btn'
			})

			try {
				const result = await testEnhancement(testPrompt, config)
				const duration = Date.now() - startTime

				document.getElementById('nig-original-prompt').textContent = result.original
				document.getElementById('nig-enhanced-prompt').textContent = result.enhanced

				logInfo('UI', 'Enhancement test completed successfully', {
					duration,
					originalLength: result.original.length,
					enhancedLength: result.enhanced.length,
					lengthChange: result.enhanced.length - result.original.length,
					userInteraction: 'test-enhancement-btn'
				})
			} catch (error) {
				const duration = Date.now() - startTime
				logError('UI', 'Enhancement test failed', {
					error: error.message,
					duration,
					testPrompt: testPrompt.substring(0, 100),
					userInteraction: 'test-enhancement-btn'
				})
				alert(`Enhancement test failed: ${error.message}`)
			} finally {
				testEnhancementBtn.disabled = false
				testEnhancementBtn.innerHTML = originalContent
			}
		})

		// Enhanced provider change listener with logging
		configPanel.querySelector('#nig-provider').addEventListener('change', async e => {
			const newProvider = e.target.value
			const oldProvider = e.target._prevProvider || 'none'
			const config = await getConfig()
			updateEnhancementUI(newProvider, config)

			logInfo('UI', 'Provider selection changed', {
				oldProvider,
				newProvider,
				enhancementEnabled: config.enhancementEnabled,
				hasApiKey: !!config.enhancementApiKey,
				providerOverride: config.enhancementOverrideProvider,
				userInteraction: 'provider-select'
			})

			e.target._prevProvider = newProvider
		})

		configPanel.querySelector('#nig-openai-compat-switch-to-select').addEventListener('click', switchToSelect)
	}

	// --- 4. CORE LOGIC ---
	let generationQueue = []
	let completedQueue = []
	let isGenerating = false
	let currentGenerationStatusText = ''
	let errorQueue = []
	let isErrorModalVisible = false

	function createErrorModal() {
		if (document.getElementById('nig-error-modal')) return
		errorModal = document.createElement('div')
		errorModal.id = 'nig-error-modal'
		errorModal.className = 'nig-modal-overlay'
		errorModal.style.display = 'none'
		errorModal.innerHTML = `
            <div class="nig-modal-content">
                <span class="nig-close-btn">&times;</span>
                <h2>Generation Failed</h2>
                <p>The image could not be generated. Please review the reason below and adjust your prompt if necessary.</p>
                <p><strong>Reason:</strong></p>
                <div id="nig-error-reason"></div>
                <p><strong>Your Prompt:</strong></p>
                <textarea id="nig-error-prompt" class="nig-error-prompt"></textarea>
                <div class="nig-form-group" style="margin-top: 15px;">
                    <label for="nig-retry-provider-select">Retry with Provider:</label>
                    <select id="nig-retry-provider-select"></select>
                </div>
                <div id="nig-error-actions" class="nig-error-actions"></div>
            </div>`
		document.body.appendChild(errorModal)
		errorModal.querySelector('.nig-close-btn').addEventListener('click', closeErrorModal)
	}

	function closeErrorModal() {
		if (errorModal) {
			const promptTextarea = document.getElementById('nig-error-prompt')
			if (promptTextarea && promptTextarea._nig_inputListener) {
				promptTextarea.removeEventListener('input', promptTextarea._nig_inputListener)
				delete promptTextarea._nig_inputListener
			}
			const providerSelect = document.getElementById('nig-retry-provider-select')
			if (providerSelect && providerSelect._nig_changeListener) {
				providerSelect.removeEventListener('change', providerSelect._nig_changeListener)
				delete providerSelect._nig_changeListener
			}
			errorModal.style.display = 'none'
		}
		isErrorModalVisible = false
		showNextError()
	}

	function showNextError() {
		if (isErrorModalVisible || errorQueue.length === 0) {
			return
		}
		const errorToShow = errorQueue.shift()
		showErrorModal(errorToShow)
	}

	async function showErrorModal(errorDetails) {
		if (!errorModal) createErrorModal()
		isErrorModalVisible = true

		document.getElementById('nig-error-reason').textContent = errorDetails.reason.message
		const promptTextarea = document.getElementById('nig-error-prompt')
		promptTextarea.value = errorDetails.prompt

		// Populate provider dropdown
		const providerSelect = document.getElementById('nig-retry-provider-select')
		providerSelect.innerHTML = ''
		const config = await getConfig()
		const providers = ['Pollinations', 'AIHorde', 'Google']
		providers.forEach(p => {
			const option = document.createElement('option')
			option.value = p
			option.textContent = p
			providerSelect.appendChild(option)
		})
		Object.keys(config.openAICompatProfiles).forEach(url => {
			const option = document.createElement('option')
			option.value = `OpenAICompat::${url}`
			option.textContent = `OpenAI: ${url.replace('https://', '').split('/')[0]}`
			providerSelect.appendChild(option)
		})

		// Pre-select the failed provider
		let failedProviderValue = errorDetails.provider
		if (errorDetails.provider === 'OpenAICompat' && errorDetails.providerProfileUrl) {
			failedProviderValue = `OpenAICompat::${errorDetails.providerProfileUrl}`
		}
		if (Array.from(providerSelect.options).some(opt => opt.value === failedProviderValue)) {
			providerSelect.value = failedProviderValue
		}

		const actionsContainer = document.getElementById('nig-error-actions')
		actionsContainer.innerHTML = ''

		const retryBtn = document.createElement('button')
		retryBtn.textContent = 'Retry Generation'
		retryBtn.className = 'nig-retry-btn'
		retryBtn.onclick = async () => {
			const editedPrompt = promptTextarea.value.trim()
			const selectedProviderValue = providerSelect.value
			let provider, providerProfileUrl

			if (selectedProviderValue.startsWith('OpenAICompat::')) {
				provider = 'OpenAICompat'
				providerProfileUrl = selectedProviderValue.split('::')[1]
			} else {
				provider = selectedProviderValue
				providerProfileUrl = null
			}

			if (editedPrompt) {
				generationQueue.unshift({prompt: editedPrompt, provider, providerProfileUrl})
				isGenerating = false
				closeErrorModal()
				updateSystemStatus()
				processQueue()
			} else {
				alert('Prompt cannot be empty.')
			}
		}

		if (errorDetails.reason.retryable) {
			actionsContainer.appendChild(retryBtn)
		} else {
			const showRetryButton = () => {
				if (!actionsContainer.contains(retryBtn)) {
					actionsContainer.appendChild(retryBtn)
				}
			}
			promptTextarea.addEventListener('input', showRetryButton)
			promptTextarea._nig_inputListener = showRetryButton
			providerSelect.addEventListener('change', showRetryButton)
			providerSelect._nig_changeListener = showRetryButton
		}

		errorModal.style.display = 'flex'
	}

	function parseErrorMessage(errorString, provider = null, providerProfileUrl = null) {
		let messageContent = String(errorString)
		const lowerCaseContent = messageContent.toLowerCase()

		if (lowerCaseContent.includes('error code: 524') || lowerCaseContent.includes('timed out') || lowerCaseContent.includes('502 bad gateway') || lowerCaseContent.includes('unable to reach the origin service')) {
			return {
				message: 'The generation service is temporarily unavailable or busy (e.g., 502 Bad Gateway). This is usually a temporary issue. Please try again in a few minutes.',
				retryable: true
			}
		}

		// Check for specific OpenAI Compatible provider false positive error from api.mnnai.ru
		if (provider === 'OpenAICompat' && providerProfileUrl && providerProfileUrl.includes('api.mnnai.ru')) {
			try {
				const errorJson = JSON.parse(messageContent.substring(messageContent.indexOf('{')))
				if (errorJson.error && errorJson.error === "Sorry, there's been some kind of mistake, please use a different model") {
					console.log('[NIG] Detected specific false positive error from api.mnnai.ru endpoint - treating as retryable')
					return {
						message: 'Temporary service error detected. The same prompt typically works on retry. This error will be automatically retried.',
						retryable: true
					}
				}
			} catch (e) {
				// Fall through to other error handling if JSON parsing fails
			}
		}

		if (lowerCaseContent.includes('unsafe content') || lowerCaseContent.includes('safety system') || lowerCaseContent.includes('moderation_blocked') || lowerCaseContent.includes('violence') || lowerCaseContent.includes('sexual')) {
			try {
				const errorJson = JSON.parse(messageContent.substring(messageContent.indexOf('{')))
				let specificMessage = errorJson.message || (errorJson.error ? errorJson.error.message : null)
				if (specificMessage) {
					specificMessage = specificMessage.replace(/If you believe this is an error, contact us at.*$/, '').trim()
					return {message: specificMessage, retryable: false}
				}
			} catch (e) {
				/* Fall through */
			}
			return {
				message: 'The prompt was rejected by the safety system for containing potentially unsafe content.',
				retryable: false
			}
		}
		try {
			const errorJson = JSON.parse(messageContent.substring(messageContent.indexOf('{')))
			const message = errorJson.message || (errorJson.error ? errorJson.error.message : null) || JSON.stringify(errorJson)
			return {message: typeof message === 'object' ? JSON.stringify(message) : message, retryable: false}
		} catch (e) {
			return {message: messageContent || 'An unknown error occurred.', retryable: false}
		}
	}

	function updateStatusWidget(state, text, onClickHandler = null) {
		if (!statusWidget) return
		statusWidget.classList.remove('loading', 'success', 'error')
		statusWidget.onclick = onClickHandler

		if (state === 'hidden') {
			statusWidget.style.display = 'none'
			return
		}
		statusWidget.style.display = 'flex'
		statusWidget.querySelector('.nig-status-text').textContent = text
		statusWidget.classList.add(state)
		const icon = statusWidget.querySelector('.nig-status-icon')
		icon.innerHTML = ''
		if (state === 'success') {
			icon.innerHTML = '✅'
		} else if (state === 'error') {
			icon.innerHTML = '❌'
		}
	}

	function updateSystemStatus() {
		if (completedQueue.length > 0) {
			const text = completedQueue.length === 1 ? '1 Image Ready! Click to view.' : `${completedQueue.length} Images Ready! Click to view.`
			updateStatusWidget('success', text, () => {
				const result = completedQueue.shift()
				if (result) {
					showImageViewer(result.imageUrls, result.prompt, result.provider)
				}
				updateSystemStatus()
			})
		} else if (isGenerating || generationQueue.length > 0) {
			const queueText = generationQueue.length > 0 ? ` (Queue: ${generationQueue.length})` : ''
			updateStatusWidget('loading', `${currentGenerationStatusText}${queueText}`)
		} else {
			updateStatusWidget('hidden', '')
		}
	}

	function handleGenerationSuccess(displayUrls, prompt, provider, model, persistentUrls = null) {
		completedQueue.push({imageUrls: displayUrls, prompt, provider})
		const historyUrls = persistentUrls || displayUrls
		historyUrls.forEach(url => addToHistory({date: new Date().toISOString(), prompt, url, provider, model}))
		isGenerating = false
		updateSystemStatus()
		processQueue()
	}

	function handleGenerationFailure(errorMessage, prompt = 'Unknown', provider, providerProfileUrl = null) {
		console.error(`Generation Failed for prompt "${prompt}" with ${provider}:`, errorMessage)
		const friendlyError = parseErrorMessage(errorMessage, provider, providerProfileUrl)
		errorQueue.push({reason: friendlyError, prompt, provider, providerProfileUrl})
		showNextError()
		updateStatusWidget('error', 'Generation Failed.')
		isGenerating = false
		setTimeout(() => {
			updateSystemStatus()
			processQueue()
		}, 3000)
	}

	async function processQueue() {
		if (isGenerating || generationQueue.length === 0) {
			return
		}
		isGenerating = true
		const request = generationQueue.shift()
		currentGenerationStatusText = 'Requesting...'
		updateSystemStatus()

		const config = await getConfig()
		if (request.provider === 'Google') {
			generateImageGoogle(request.prompt)
		} else if (request.provider === 'AIHorde') {
			generateImageAIHorde(request.prompt)
		} else if (request.provider === 'Pollinations') {
			generateImagePollinations(request.prompt)
		} else if (request.provider === 'OpenAICompat') {
			generateImageOpenAICompat(request.prompt, request.providerProfileUrl)
		}
	}

	function handleSelection() {
		const selection = window.getSelection()
		if (!selection || selection.rangeCount === 0) {
			generateBtn.style.display = 'none'
			return
		}
		const selectedText = selection.toString().trim()

		if (selectedText.length > 5) {
			currentSelection = selectedText
			const range = selection.getRangeAt(0)
			const rects = range.getClientRects()

			if (rects.length === 0) {
				generateBtn.style.display = 'none'
				return
			}

			const firstRect = rects[0]
			const lastRect = rects[rects.length - 1]

			generateBtn.style.display = 'block'
			generateBtn.style.visibility = 'hidden'
			const buttonHeight = generateBtn.offsetHeight
			generateBtn.style.visibility = 'visible'

			let topPosition = window.scrollY + firstRect.top - buttonHeight - 5

			if (topPosition < window.scrollY) {
				topPosition = window.scrollY + lastRect.bottom + 5
			}

			generateBtn.style.top = `${topPosition}px`
			generateBtn.style.left = `${window.scrollX + firstRect.left}px`
		} else {
			generateBtn.style.display = 'none'
		}
	}

	async function onGenerateClick() {
		const generationStartTime = Date.now()
		generateBtn.style.display = 'none'

		logInfo('GENERATION', 'Starting image generation process', {
			hasSelection: !!currentSelection,
			selectionLength: currentSelection ? currentSelection.length : 0,
			selectionPreview: currentSelection ? currentSelection.substring(0, 100) + (currentSelection.length > 100 ? '...' : '') : 'none'
		})

		if (window.getSelection) {
			window.getSelection().removeAllRanges()
		}

		if (!currentSelection) {
			logWarn('GENERATION', 'No text selected for generation')
			return
		}

		const config = await getConfig()

		logDebug('GENERATION', 'Configuration loaded', {
			provider: config.selectedProvider,
			enhancementEnabled: config.enhancementEnabled,
			enhancementApiKeyPresent: !!config.enhancementApiKey,
			customStyleEnabled: config.customStyleEnabled,
			mainPromptStyle: config.mainPromptStyle,
			subPromptStyle: config.subPromptStyle,
			enableNegPrompt: config.enableNegPrompt
		})

		let finalPrompt = currentSelection
		let prefix = ''
		let promptLogDetails = {
			originalPrompt: currentSelection,
			originalLength: currentSelection.length,
			hasPrefix: false,
			prefixApplied: '',
			enhancementApplied: false,
			enhancementSource: 'none',
			enhancementDuration: null,
			negativePromptAdded: false,
			finalLength: 0
		}

		// Apply prompt styling
		if (config.customStyleEnabled && config.customStyleText) {
			prefix = config.customStyleText.trim()
			if (prefix && !prefix.endsWith(', ')) prefix += ', '
			logInfo('STYLING', 'Applied custom style prefix', {
				prefix,
				prefixLength: prefix.length
			})
		} else if (config.mainPromptStyle !== 'None') {
			if (config.subPromptStyle && config.subPromptStyle !== 'none') {
				prefix = config.subPromptStyle
				logInfo('STYLING', 'Applied sub-style prefix', {
					prefix,
					mainStyle: config.mainPromptStyle,
					subStyle: config.subPromptStyle
				})
			} else {
				prefix = `${config.mainPromptStyle} style, `
				logInfo('STYLING', 'Applied main style prefix', {
					prefix,
					mainStyle: config.mainPromptStyle
				})
			}
		}

		promptLogDetails.hasPrefix = !!prefix
		promptLogDetails.prefixApplied = prefix
		finalPrompt = prefix + finalPrompt

		logInfo('STYLING', 'Prompt styling completed', {
			originalLength: currentSelection.length,
			styledLength: finalPrompt.length,
			prefixLength: prefix.length,
			lengthIncrease: finalPrompt.length - currentSelection.length
		})

		// Apply prompt enhancement if enabled
		if (config.enhancementEnabled) {
			logInfo('ENHANCEMENT', 'Checking enhancement eligibility', {
				enhancementEnabled: config.enhancementEnabled,
				hasApiKey: !!config.enhancementApiKey,
				provider: config.selectedProvider
			})

			const shouldUseProviderEnh = shouldUseProviderEnhancement(config.selectedProvider, config)
			const hasApiKey = config.enhancementApiKey.trim().length > 0

			// Use external AI enhancement if:
			// 1. Provider doesn't have built-in enhancement, OR
			// 2. User has overridden provider priority
			const shouldUseExternalEnhancement = (!shouldUseProviderEnh || config.enhancementOverrideProvider) && hasApiKey

			logInfo('ENHANCEMENT', 'Enhancement decision made', {
				shouldUseProviderEnh,
				hasApiKey,
				enhancementOverrideProvider: config.enhancementOverrideProvider,
				shouldUseExternalEnhancement,
				decision: shouldUseExternalEnhancement ? 'Using external AI enhancement' : shouldUseProviderEnh ? 'Using provider enhancement' : 'No enhancement'
			})

			if (shouldUseExternalEnhancement) {
				const enhancementStartTime = Date.now()
				try {
					logDebug('ENHANCEMENT', 'Starting external AI enhancement')
					updateStatusWidget('loading', 'Enhancing prompt...')

					const enhancedPrompt = await enhancePromptWithGemini(finalPrompt, config)
					const enhancementDuration = Date.now() - enhancementStartTime

					promptLogDetails.enhancementApplied = true
					promptLogDetails.enhancementSource = 'external-ai'
					promptLogDetails.enhancementDuration = enhancementDuration

					logInfo('ENHANCEMENT', 'External AI enhancement completed successfully', {
						duration: enhancementDuration,
						originalLength: finalPrompt.length,
						enhancedLength: enhancedPrompt.length,
						lengthChange: enhancedPrompt.length - finalPrompt.length,
						improvementRatio: (enhancedPrompt.length / finalPrompt.length).toFixed(2)
					})

					finalPrompt = enhancedPrompt
					updateStatusWidget('success', 'Prompt enhanced!')
					setTimeout(() => updateSystemStatus(), 2000)
				} catch (error) {
					const enhancementDuration = Date.now() - enhancementStartTime
					logError('ENHANCEMENT', 'External AI enhancement failed, falling back to original', {
						error: error.message,
						duration: enhancementDuration,
						originalPrompt: finalPrompt.substring(0, 100),
						willUseOriginalPrompt: true
					})

					updateStatusWidget('error', 'Enhancement failed, using original prompt')
					setTimeout(() => updateSystemStatus(), 3000)
				}
			} else if (shouldUseProviderEnh) {
				promptLogDetails.enhancementApplied = true
				promptLogDetails.enhancementSource = 'provider'
				logInfo('ENHANCEMENT', 'Using provider built-in enhancement', {
					provider: config.selectedProvider,
					enhancementMethod: 'provider-built-in'
				})
			} else {
				logInfo('ENHANCEMENT', 'No enhancement will be applied', {
					reason: !hasApiKey ? 'No API key' : !shouldUseProviderEnh ? 'Provider not supported' : 'User override disabled'
				})
			}
			// If provider has enhancement or enhancement failed, use original prompt
		}

		// Append global negative prompt
		if (config.enableNegPrompt && config.globalNegPrompt && config.selectedProvider !== 'AIHorde') {
			finalPrompt += `, negative prompt: ${config.globalNegPrompt}`
			promptLogDetails.negativePromptAdded = true
			logDebug('NEGATIVE', 'Global negative prompt added', {
				negativePrompt: config.globalNegPrompt,
				originalLength: finalPrompt.length - config.globalNegPrompt.length - 18, // subtracting ", negative prompt: " length
				newLength: finalPrompt.length
			})
		}

		promptLogDetails.finalLength = finalPrompt.length
		const totalDuration = Date.now() - generationStartTime

		logInfo('GENERATION', 'Prompt preparation completed', {
			totalDuration,
			...promptLogDetails,
			summary: {
				styled: promptLogDetails.hasPrefix,
				enhanced: promptLogDetails.enhancementApplied,
				negativeAdded: promptLogDetails.negativePromptAdded,
				enhancementSource: promptLogDetails.enhancementSource
			}
		})

		if (config.selectedProvider === 'Google' && !config.googleApiKey) {
			logWarn('GENERATION', 'Google provider selected but no API key provided')
			showGoogleApiKeyPrompt()
			return
		}

		const queueEntry = {
			prompt: finalPrompt,
			provider: config.selectedProvider,
			providerProfileUrl: config.openAICompatActiveProfileUrl,
			logDetails: promptLogDetails
		}

		logInfo('GENERATION', 'Adding generation to queue', {
			provider: config.selectedProvider,
			promptLength: finalPrompt.length,
			queueLength: generationQueue.length + 1,
			estimatedTotalDuration: totalDuration
		})

		generationQueue.push(queueEntry)
		updateSystemStatus()
		processQueue()
	}

	function createImageViewer() {
		if (document.getElementById('nig-image-viewer')) return
		imageViewer = document.createElement('div')
		imageViewer.id = 'nig-image-viewer'
		imageViewer.className = 'nig-modal-overlay'
		imageViewer.style.display = 'none'
		imageViewer.innerHTML = `
            <div class="nig-modal-content">
                <span class="nig-close-btn">&times;</span>
                <div id="nig-prompt-container" class="nig-prompt-container">
                    <div class="nig-prompt-header"><span>Generated Image Prompt</span></div>
                    <p id="nig-prompt-text" class="nig-prompt-text"></p>
                </div>
                <div id="nig-image-gallery" class="nig-image-gallery"></div>
            </div>`
		document.body.appendChild(imageViewer)
		imageViewer.querySelector('.nig-close-btn').addEventListener('click', () => {
			imageViewer.style.display = 'none'
			updateSystemStatus()
		})
		const promptContainer = imageViewer.querySelector('#nig-prompt-container')
		promptContainer.addEventListener('click', () => {
			promptContainer.classList.toggle('expanded')
		})
	}

	function showGoogleApiKeyPrompt() {
		if (document.getElementById('nig-google-api-prompt')) return
		googleApiPrompt = document.createElement('div')
		googleApiPrompt.id = 'nig-google-api-prompt'
		googleApiPrompt.className = 'nig-modal-overlay'
		googleApiPrompt.innerHTML = `<div class="nig-modal-content"><span class="nig-close-btn">&times;</span><h2>Google API Key Required</h2><p>Please provide your Google AI Gemini API key. You can get one from <a href="https://aistudio.google.com/api-keys" target="_blank" class="nig-api-prompt-link">Google AI Studio</a>.</p><div class="nig-form-group"><label for="nig-prompt-api-key">Gemini API Key</label><input type="password" id="nig-prompt-api-key"></div><button id="nig-prompt-save-btn" class="nig-save-btn">Save Key</button></div>`
		document.body.appendChild(googleApiPrompt)
		googleApiPrompt.querySelector('.nig-close-btn').addEventListener('click', () => googleApiPrompt.remove())
		googleApiPrompt.querySelector('#nig-prompt-save-btn').addEventListener('click', async () => {
			const key = googleApiPrompt.querySelector('#nig-prompt-api-key').value.trim()
			if (key) {
				await setConfig('googleApiKey', key)
				googleApiPrompt.remove()
				alert('API Key saved. You can now generate an image.')
			} else {
				alert('API Key cannot be empty.')
			}
		})
	}

	function showPollinationsAuthPrompt(errorMessage, failedPrompt) {
		if (document.getElementById('nig-pollinations-auth-prompt')) return
		pollinationsAuthPrompt = document.createElement('div')
		pollinationsAuthPrompt.id = 'nig-pollinations-auth-prompt'
		pollinationsAuthPrompt.className = 'nig-modal-overlay'
		pollinationsAuthPrompt.innerHTML = `
            <div class="nig-modal-content">
                <span class="nig-close-btn">&times;</span>
                <h2>Authentication Required</h2>
                <p>The Pollinations.ai model you selected requires authentication. You can get free access by registering.</p>
                <p><strong>Error Message:</strong> <em>${errorMessage}</em></p>
                <p>Please visit <a href="https://auth.pollinations.ai" target="_blank" class="nig-api-prompt-link">auth.pollinations.ai</a> to continue. You can either:</p>
                <ul>
                    <li><strong>Register the Referrer:</strong> The easiest method. Just register the domain <code>wtr-lab.com</code>. This links your usage to your account without needing a token.</li>
                    <li><strong>Use a Token:</strong> Get an API token and enter it below.</li>
                </ul>
                <div class="nig-form-group">
                    <label for="nig-prompt-pollinations-token">Pollinations API Token</label>
                    <input type="password" id="nig-prompt-pollinations-token">
                </div>
                <button id="nig-prompt-save-token-btn" class="nig-save-btn">Save Token & Retry</button>
            </div>`
		document.body.appendChild(pollinationsAuthPrompt)

		pollinationsAuthPrompt.querySelector('.nig-close-btn').addEventListener('click', () => pollinationsAuthPrompt.remove())
		pollinationsAuthPrompt.querySelector('#nig-prompt-save-token-btn').addEventListener('click', async () => {
			const token = pollinationsAuthPrompt.querySelector('#nig-prompt-pollinations-token').value.trim()
			if (token) {
				await setConfig('pollinationsToken', token)
				pollinationsAuthPrompt.remove()
				alert('Token saved. Retrying generation...')
				generationQueue.unshift({prompt: failedPrompt, provider: 'Pollinations'})
				isGenerating = false
				processQueue()
			} else {
				alert('Token cannot be empty.')
			}
		})
	}

	function showImageViewer(imageUrls, prompt, provider) {
		if (!imageViewer) createImageViewer()
		const gallery = imageViewer.querySelector('#nig-image-gallery')
		gallery.innerHTML = ''
		const promptContainer = imageViewer.querySelector('#nig-prompt-container')
		const promptText = imageViewer.querySelector('#nig-prompt-text')
		promptText.textContent = prompt
		promptContainer.classList.remove('expanded')
		const extension = provider === 'Pollinations' || provider === 'OpenAICompat' ? 'jpg' : 'png'
		imageUrls.forEach((url, index) => {
			const container = document.createElement('div')
			container.className = 'nig-image-container'
			const img = document.createElement('img')
			img.src = url
			const actions = document.createElement('div')
			actions.className = 'nig-image-actions'
			const downloadBtn = document.createElement('button')
			downloadBtn.innerHTML = '<span class="material-symbols-outlined">download</span>'
			downloadBtn.title = 'Download'
			downloadBtn.onclick = () => {
				const a = document.createElement('a')
				a.href = url
				const scriptName = getScriptName()
				const promptSnippet = prompt.substring(0, 20).replace(/\s/g, '_')
				a.download = `${scriptName}_${promptSnippet}_${index}.${extension}`
				a.click()
			}
			const fullscreenBtn = document.createElement('button')
			fullscreenBtn.innerHTML = '<span class="material-symbols-outlined">fullscreen</span>'
			fullscreenBtn.title = 'Fullscreen'
			fullscreenBtn.onclick = () => {
				if (img.requestFullscreen) img.requestFullscreen()
			}
			actions.appendChild(downloadBtn)
			actions.appendChild(fullscreenBtn)
			container.appendChild(img)
			container.appendChild(actions)
			gallery.appendChild(container)
		})
		imageViewer.style.display = 'flex'
	}

	async function generateImageGoogle(prompt) {
		currentGenerationStatusText = 'Generating with Google...'
		updateSystemStatus()
		const config = await getConfig()
		const model = config.model
		const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:predict`
		const parameters = {
			sampleCount: parseInt(config.numberOfImages, 10),
			aspectRatio: config.aspectRatio,
			personGeneration: config.personGeneration
		}
		if (!model.includes('fast')) {
			parameters.imageSize = parseInt(config.imageSize, 10)
		}
		GM_xmlhttpRequest({
			method: 'POST',
			url,
			headers: {'x-goog-api-key': config.googleApiKey, 'Content-Type': 'application/json'},
			data: JSON.stringify({instances: [{prompt}], parameters}),
			onload: response => {
				try {
					const data = JSON.parse(response.responseText)
					if (data.error) throw new Error(JSON.stringify(data.error))
					const imageUrls = data.predictions.map(p => `data:image/png;base64,${p.bytesB64Encoded}`)
					handleGenerationSuccess(imageUrls, prompt, 'Google', model)
				} catch (e) {
					handleGenerationFailure(e.message, prompt, 'Google')
				}
			},
			onerror: error => {
				handleGenerationFailure(JSON.stringify(error), prompt, 'Google')
			}
		})
	}

	async function generateImageAIHorde(prompt) {
		const config = await getConfig()
		const apiKey = config.aiHordeApiKey || '0000000000'
		const model = config.aiHordeModel
		const params = {
			shared: true,
			sampler_name: config.aiHordeSampler,
			cfg_scale: parseFloat(config.aiHordeCfgScale),
			steps: parseInt(config.aiHordeSteps, 10),
			width: parseInt(config.aiHordeWidth, 10),
			height: parseInt(config.aiHordeHeight, 10)
		}
		if (config.aiHordeSeed) params.seed = config.aiHordeSeed
		if (config.aiHordePostProcessing.length > 0) params.post_processing = config.aiHordePostProcessing
		const payload = {prompt: prompt, params: params, models: [model]}
		if (config.enableNegPrompt && config.globalNegPrompt) payload.negative_prompt = config.globalNegPrompt
		GM_xmlhttpRequest({
			method: 'POST',
			url: 'https://aihorde.net/api/v2/generate/async',
			headers: {'Content-Type': 'application/json', apikey: apiKey},
			data: JSON.stringify(payload),
			onload: response => {
				try {
					const data = JSON.parse(response.responseText)
					if (data.id) {
						checkAIHordeStatus(data.id, prompt, Date.now(), model)
					} else {
						if (data.message && data.message.toLowerCase().includes('model')) {
							handleGenerationFailure(`Model error: ${data.message}. Refreshing model list.`, prompt, 'AIHorde')
							clearCachedModels('aiHorde')
							return
						}
						throw new Error(data.message || 'Failed to initiate generation.')
					}
				} catch (e) {
					handleGenerationFailure(e.message, prompt, 'AIHorde')
				}
			},
			onerror: error => {
				handleGenerationFailure(JSON.stringify(error), prompt, 'AIHorde')
			}
		})
	}

	function checkAIHordeStatus(id, prompt, startTime, model) {
		if (Date.now() - startTime > 300000) {
			handleGenerationFailure('Timed out after 5 minutes.', prompt, 'AIHorde')
			return
		}
		GM_xmlhttpRequest({
			method: 'GET',
			url: `https://aihorde.net/api/v2/generate/status/${id}`,
			onload: response => {
				try {
					const data = JSON.parse(response.responseText)
					if (data.done) {
						const imageUrls = data.generations.map(gen => gen.img)
						handleGenerationSuccess(imageUrls, prompt, 'AIHorde', model)
					} else {
						let statusText = `Waiting for worker...`
						if (data.queue_position > 0) {
							statusText = `Queue: ${data.queue_position}. Est: ${data.wait_time}s.`
						}
						if (data.processing > 0) {
							statusText = `Generating...`
						}
						currentGenerationStatusText = statusText
						updateSystemStatus()
						setTimeout(() => checkAIHordeStatus(id, prompt, startTime, model), 5000)
					}
				} catch (e) {
					handleGenerationFailure(`Error checking status: ${e.message}`, prompt, 'AIHorde')
				}
			},
			onerror: error => {
				handleGenerationFailure('Failed to get status from AI Horde.', prompt, 'AIHorde')
			}
		})
	}

	async function generateImagePollinations(prompt) {
		currentGenerationStatusText = 'Generating with Pollinations...'
		updateSystemStatus()
		const config = await getConfig()
		const model = config.pollinationsModel
		const encodedPrompt = encodeURIComponent(prompt)
		const params = new URLSearchParams()

		if (config.pollinationsToken) params.append('token', config.pollinationsToken)
		if (model && model !== 'flux') params.append('model', model)
		if (config.pollinationsWidth && config.pollinationsWidth != 1024) params.append('width', config.pollinationsWidth)
		if (config.pollinationsHeight && config.pollinationsHeight != 1024) params.append('height', config.pollinationsHeight)
		if (config.pollinationsSeed) params.append('seed', config.pollinationsSeed)
		if (config.pollinationsEnhance) params.append('enhance', 'true')
		if (config.pollinationsSafe) params.append('safe', 'true')
		if (config.pollinationsNologo) params.append('nologo', 'true')
		if (config.pollinationsPrivate) params.append('private', 'true')

		const paramString = params.toString()
		const url = `https://image.pollinations.ai/prompt/${encodedPrompt}${paramString ? '?' + paramString : ''}`

		GM_xmlhttpRequest({
			method: 'GET',
			url: url,
			responseType: 'blob',
			headers: {
				'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
			},
			onload: response => {
				if (response.status >= 200 && response.status < 300) {
					const blobUrl = URL.createObjectURL(response.response)
					handleGenerationSuccess([blobUrl], prompt, 'Pollinations', model, [url])
				} else {
					const handleErrorResponse = async () => {
						try {
							const text = await response.response.text()
							if (text.toLowerCase().includes('model not found')) {
								handleGenerationFailure(`Model error: ${text}. Refreshing model list.`, prompt, 'Pollinations')
								clearCachedModels('pollinations')
								return
							}
							if (response.status === 403) {
								try {
									const errorJson = JSON.parse(text)
									if (errorJson.message && errorJson.message.includes('authenticate at https://auth.pollinations.ai')) {
										showPollinationsAuthPrompt(errorJson.message, prompt)
										isGenerating = false
										updateStatusWidget('error', 'Authentication needed.')
										setTimeout(() => updateSystemStatus(), 4000)
										return
									}
								} catch (jsonError) {
									/* Not a JSON error, fall through */
								}
							}
							handleGenerationFailure(`Error ${response.status}: ${text}`, prompt, 'Pollinations')
						} catch (e) {
							handleGenerationFailure(`Error ${response.status}: ${response.statusText}`, prompt, 'Pollinations')
						}
					}
					handleErrorResponse()
				}
			},
			onerror: error => {
				handleGenerationFailure(JSON.stringify(error), prompt, 'Pollinations')
			}
		})
	}

	async function generateImageOpenAICompat(prompt, providerProfileUrl = null) {
		currentGenerationStatusText = 'Generating with OpenAI Compatible API...'
		updateSystemStatus()
		const config = await getConfig()
		const profiles = config.openAICompatProfiles
		const activeUrl = providerProfileUrl || config.openAICompatActiveProfileUrl
		const activeProfile = profiles[activeUrl]

		if (!activeProfile) {
			handleGenerationFailure(`No active or valid OpenAI Compatible profile found for URL: ${activeUrl}`, prompt, 'OpenAICompat')
			return
		}

		const url = `${activeUrl}/images/generations`
		const payload = {
			model: activeProfile.model,
			prompt: prompt,
			n: 1,
			size: '1024x1024',
			response_format: 'b64_json'
		}
		GM_xmlhttpRequest({
			method: 'POST',
			url: url,
			headers: {
				'Content-Type': 'application/json',
				Authorization: `Bearer ${activeProfile.apiKey}`
			},
			data: JSON.stringify(payload),
			onload: response => {
				try {
					const data = JSON.parse(response.responseText)
					if (data && Array.isArray(data.data) && data.data.length > 0) {
						const imageUrls = data.data
							.map(item => {
								if (item.b64_json) {
									return `data:image/png;base64,${item.b64_json}`
								} else if (item.url) {
									return item.url
								}
								return null
							})
							.filter(url => url !== null)

						if (imageUrls.length > 0) {
							handleGenerationSuccess(imageUrls, prompt, 'OpenAICompat', activeProfile.model)
						} else {
							throw new Error('API response did not contain usable image data (b64_json or url).')
						}
					} else {
						throw new Error(JSON.stringify(data))
					}
				} catch (e) {
					handleGenerationFailure(e.message, prompt, 'OpenAICompat', activeUrl)
				}
			},
			onerror: error => {
				handleGenerationFailure(JSON.stringify(error), prompt, 'OpenAICompat', activeUrl)
			}
		})
	}

	function updateVisibleSettings() {
		const provider = document.getElementById('nig-provider').value
		document.querySelectorAll('.nig-provider-settings').forEach(el => (el.style.display = 'none'))
		const settingsEl = document.getElementById(`nig-provider-${provider}`)
		if (settingsEl) {
			settingsEl.style.display = 'block'
		}
	}

	function populatePollinationsSelect(select, models, selectedModel) {
		select.innerHTML = ''
		models.forEach(model => {
			const option = document.createElement('option')
			option.value = model
			let textContent = model
			if (model === 'gptimage') {
				textContent += ' (Recommended: Quality)'
			} else if (model === 'flux') {
				textContent += ' (Default: Speed)'
			}
			option.textContent = textContent
			select.appendChild(option)
		})
		if (models.includes(selectedModel)) {
			select.value = selectedModel
		}
	}

	async function fetchPollinationsModels(selectedModel) {
		const select = document.getElementById('nig-pollinations-model')
		const cache = await getCachedModels()

		if (cache.pollinations && cache.pollinations.length > 0) {
			logInfo('CACHE', 'Loading Pollinations models from cache.')
			populatePollinationsSelect(select, cache.pollinations, selectedModel)
			return
		}

		select.innerHTML = '<option>Fetching models...</option>'
		GM_xmlhttpRequest({
			method: 'GET',
			url: 'https://image.pollinations.ai/models',
			headers: {
				'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
			},
			onload: async response => {
				try {
					const models = JSON.parse(response.responseText)
					await setCachedModels('pollinations', models)
					log('Fetched and cached Pollinations models.')
					populatePollinationsSelect(select, models, selectedModel)
				} catch (e) {
					select.innerHTML = '<option>Failed to load models</option>'
					console.error('Failed to parse Pollinations models:', e)
				}
			},
			onerror: () => {
				select.innerHTML = '<option>Failed to load models</option>'
			}
		})
	}

	async function fetchAIHordeModels(selectedModel) {
		const select = document.getElementById('nig-horde-model')
		const cache = await getCachedModels()

		const populateSelect = models => {
			select.innerHTML = ''
			const apiModelMap = new Map(models.map(m => [m.name, m]))
			const topModelNames = new Set(TOP_MODELS.map(m => m.name))
			const topGroup = document.createElement('optgroup')
			topGroup.label = 'Top 10 Popular Models'
			const otherGroup = document.createElement('optgroup')
			otherGroup.label = 'Other Models'
			TOP_MODELS.forEach(topModel => {
				if (apiModelMap.has(topModel.name)) {
					const apiData = apiModelMap.get(topModel.name)
					const option = document.createElement('option')
					option.value = topModel.name
					option.textContent = `${topModel.name} - ${topModel.desc} (${apiData.count} workers)`
					topGroup.appendChild(option)
				}
			})
			const otherModels = models.filter(m => !topModelNames.has(m.name)).sort((a, b) => b.count - a.count)
			otherModels.forEach(model => {
				const option = document.createElement('option')
				option.value = model.name
				option.textContent = `${model.name} (${model.count} workers)`
				otherGroup.appendChild(option)
			})
			select.appendChild(topGroup)
			select.appendChild(otherGroup)
			if (Array.from(select.options).some(opt => opt.value === selectedModel)) {
				select.value = selectedModel
			}
		}

		if (cache.aiHorde && cache.aiHorde.length > 0) {
			logInfo('CACHE', 'Loading AI Horde models from cache.')
			populateSelect(cache.aiHorde)
			return
		}

		select.innerHTML = '<option>Fetching models...</option>'
		GM_xmlhttpRequest({
			method: 'GET',
			url: 'https://aihorde.net/api/v2/status/models?type=image',
			onload: async response => {
				try {
					const apiModels = JSON.parse(response.responseText)
					await setCachedModels('aiHorde', apiModels)
					log('Fetched and cached AI Horde models.')
					populateSelect(apiModels)
				} catch (e) {
					select.innerHTML = '<option>Failed to load models</option>'
					console.error('Failed to parse AI Horde models:', e)
				}
			},
			onerror: () => {
				select.innerHTML = '<option>Failed to load models</option>'
			}
		})
	}

	function isModelFree(model) {
		if (typeof model.is_free === 'boolean') return model.is_free
		if (typeof model.premium_model === 'boolean') return !model.premium_model
		if (Array.isArray(model.tiers) && model.tiers.includes('Free')) return true
		return false
	}

	function populateOpenAICompatSelect(select, models, selectedModel) {
		select.innerHTML = ''
		const freeGroup = document.createElement('optgroup')
		freeGroup.label = 'Free Models'
		const paidGroup = document.createElement('optgroup')
		paidGroup.label = 'Paid Models'

		models.forEach(model => {
			const option = document.createElement('option')
			option.value = model.id
			option.textContent = model.id
			if (isModelFree(model)) {
				freeGroup.appendChild(option)
			} else {
				paidGroup.appendChild(option)
			}
		})

		if (freeGroup.childElementCount > 0) select.appendChild(freeGroup)
		if (paidGroup.childElementCount > 0) select.appendChild(paidGroup)

		if (models.some(m => m.id === selectedModel)) {
			select.value = selectedModel
		}
	}

	async function fetchOpenAICompatModels(selectedModel) {
		const select = document.getElementById('nig-openai-compat-model')
		const baseUrl = document.getElementById('nig-openai-compat-base-url').value.trim()
		const apiKey = document.getElementById('nig-openai-compat-api-key').value.trim()

		if (!baseUrl) {
			alert('Please enter a Base URL first.')
			return
		}

		const cacheKey = `openAICompat::${baseUrl}`
		const cache = await getCachedModels()
		const cachedData = cache[cacheKey]

		if (cachedData && cachedData.timestamp && Date.now() - cachedData.timestamp < CACHE_EXPIRATION_MS) {
			logInfo('CACHE', `Loading OpenAI Compatible models for ${baseUrl} from cache.`)
			populateOpenAICompatSelect(select, cachedData.models, selectedModel)
			return
		}

		const switchToManual = () => {
			document.getElementById('nig-openai-model-container-select').style.display = 'none'
			document.getElementById('nig-openai-model-container-manual').style.display = 'block'
		}

		select.innerHTML = '<option>Fetching models...</option>'
		GM_xmlhttpRequest({
			method: 'GET',
			url: `${baseUrl}/models`,
			headers: {Authorization: `Bearer ${apiKey}`},
			onload: async response => {
				try {
					const data = JSON.parse(response.responseText)
					if (!data.data || !Array.isArray(data.data)) {
						throw new Error('Invalid model list format received.')
					}

					let imageModels = []
					if (data.data.some(m => m.endpoint || m.endpoints)) {
						imageModels = data.data.filter(model => model.endpoint === '/v1/images/generations' || model.endpoints?.includes('/v1/images/generations'))
					} else if (data.data.some(m => m.type === 'images.generations')) {
						imageModels = data.data.filter(model => model.type === 'images.generations')
					} else {
						throw new Error('Could not determine image models from response.')
					}

					imageModels.sort((a, b) => {
						const aIsFree = isModelFree(a)
						const bIsFree = isModelFree(b)
						if (aIsFree && !bIsFree) return -1
						if (!aIsFree && bIsFree) return 1
						return a.id.localeCompare(b.id)
					})
					// Cache the fetched models
					await setCachedModels(cacheKey, {models: imageModels, timestamp: Date.now()})
					log(`Fetched and cached OpenAI Compatible models for ${baseUrl}.`)

					populateOpenAICompatSelect(select, imageModels, selectedModel)
				} catch (e) {
					select.innerHTML = '<option>Failed to load models</option>'
					console.error('Failed to parse OpenAI Compatible models:', e)
					alert(`Failed to fetch models. Check the Base URL and API Key. You can enter the model name manually. Error: ${e.message}`)
					switchToManual()
				}
			},
			onerror: error => {
				select.innerHTML = '<option>Failed to load models</option>'
				console.error('Error fetching OpenAI Compatible models:', error)
				alert('Failed to fetch models. Check your network connection and the Base URL. Switching to manual input.')
				switchToManual()
			}
		})
	}

	function updateSubStyles(mainStyleName) {
		const subStyleSelect = document.getElementById('nig-sub-style')
		const mainStyleDesc = document.getElementById('nig-main-style-desc')
		const subStyleDesc = document.getElementById('nig-sub-style-desc')

		const selectedCategory = PROMPT_CATEGORIES.find(cat => cat.name === mainStyleName)
		mainStyleDesc.textContent = selectedCategory ? selectedCategory.description : ''
		subStyleSelect.innerHTML = ''

		if (selectedCategory && selectedCategory.subStyles.length > 0) {
			subStyleSelect.disabled = false
			selectedCategory.subStyles.forEach(sub => {
				const option = document.createElement('option')
				option.value = sub.value
				option.textContent = sub.name
				subStyleSelect.appendChild(option)
			})
			subStyleSelect.dispatchEvent(new Event('change'))
		} else {
			subStyleSelect.disabled = true
			subStyleDesc.textContent = ''
		}
	}

	// --- OpenAI Profile Management ---
	async function loadOpenAIProfiles() {
		const config = await getConfig()
		const profiles = config.openAICompatProfiles
		const activeUrl = config.openAICompatActiveProfileUrl
		const select = document.getElementById('nig-openai-compat-profile-select')
		select.innerHTML = ''

		Object.keys(profiles).forEach(url => {
			const option = document.createElement('option')
			option.value = url
			option.textContent = url
			select.appendChild(option)
		})

		const newOption = document.createElement('option')
		newOption.value = '__new__'
		newOption.textContent = '— Add or Edit Profile —'
		select.appendChild(newOption)

		if (activeUrl && profiles[activeUrl]) {
			select.value = activeUrl
		} else {
			select.value = '__new__'
		}
		loadSelectedOpenAIProfile()
	}

	async function loadSelectedOpenAIProfile() {
		const select = document.getElementById('nig-openai-compat-profile-select')
		const selectedUrl = select.value
		const profiles = await GM_getValue('openAICompatProfiles', {})
		const profile = profiles[selectedUrl] || {apiKey: '', model: ''}

		document.getElementById('nig-openai-compat-base-url').value = selectedUrl === '__new__' ? '' : selectedUrl
		document.getElementById('nig-openai-compat-api-key').value = profile.apiKey
		document.getElementById('nig-openai-compat-model-manual').value = profile.model

		if (selectedUrl !== '__new__') {
			fetchOpenAICompatModels(profile.model)
		} else {
			document.getElementById('nig-openai-compat-model').innerHTML = '<option>Enter URL/Key and fetch...</option>'
		}
	}

	async function deleteSelectedOpenAIProfile() {
		const select = document.getElementById('nig-openai-compat-profile-select')
		const urlToDelete = select.value
		if (urlToDelete === '__new__') {
			alert("You can't delete the 'Add New' option.")
			return
		}
		if (confirm(`Are you sure you want to delete the profile for "${urlToDelete}"?`)) {
			const profiles = await GM_getValue('openAICompatProfiles', {})
			delete profiles[urlToDelete]
			await GM_setValue('openAICompatProfiles', profiles)
			await loadOpenAIProfiles()
		}
	}

	async function populateConfigForm() {
		const config = await getConfig()
		document.getElementById('nig-provider').value = config.selectedProvider

		// --- Populate Prompt Styling Tab ---
		const mainStyleSelect = document.getElementById('nig-main-style')
		const subStyleSelect = document.getElementById('nig-sub-style')
		const subStyleDesc = document.getElementById('nig-sub-style-desc')
		const customStyleEnable = document.getElementById('nig-custom-style-enable')
		const customStyleText = document.getElementById('nig-custom-style-text')

		mainStyleSelect.innerHTML = ''
		PROMPT_CATEGORIES.forEach(cat => {
			const option = document.createElement('option')
			option.value = cat.name
			option.textContent = cat.name
			mainStyleSelect.appendChild(option)
		})

		mainStyleSelect.value = config.mainPromptStyle
		updateSubStyles(config.mainPromptStyle)
		subStyleSelect.value = config.subPromptStyle

		mainStyleSelect.addEventListener('change', () => updateSubStyles(mainStyleSelect.value))
		subStyleSelect.addEventListener('change', () => {
			const category = PROMPT_CATEGORIES.find(c => c.name === mainStyleSelect.value)
			if (category) {
				const subStyle = category.subStyles.find(s => s.value === subStyleSelect.value)
				subStyleDesc.textContent = subStyle ? subStyle.description : ''
			}
		})
		subStyleSelect.dispatchEvent(new Event('change'))

		customStyleEnable.checked = config.customStyleEnabled
		customStyleText.value = config.customStyleText
		customStyleText.disabled = !config.customStyleEnabled

		// AI Prompt Enhancement Settings
		document.getElementById('nig-enhancement-enabled').checked = config.enhancementEnabled
		document.getElementById('nig-gemini-api-key').value = config.enhancementApiKey
		document.getElementById('nig-enhancement-model').value = config.enhancementModel
		document.getElementById('nig-enhancement-template').value = config.enhancementTemplate

		// Initialize enhancement UI state
		toggleEnhancementSettings(config.enhancementEnabled)
		updateEnhancementUI(config.selectedProvider, config)

		// Show preview if API key is provided
		if (config.enhancementApiKey.trim().length > 0) {
			document.getElementById('nig-enhancement-preview').style.display = 'block'
		}

		// Global Negative Prompting
		document.getElementById('nig-enable-neg-prompt').checked = config.enableNegPrompt
		document.getElementById('nig-global-neg-prompt').value = config.globalNegPrompt

		// Pollinations
		document.getElementById('nig-pollinations-width').value = config.pollinationsWidth
		document.getElementById('nig-pollinations-height').value = config.pollinationsHeight
		document.getElementById('nig-pollinations-seed').value = config.pollinationsSeed
		document.getElementById('nig-pollinations-enhance').checked = config.pollinationsEnhance
		document.getElementById('nig-pollinations-safe').checked = config.pollinationsSafe
		document.getElementById('nig-pollinations-nologo').checked = config.pollinationsNologo
		document.getElementById('nig-pollinations-private').checked = config.pollinationsPrivate
		document.getElementById('nig-pollinations-token').value = config.pollinationsToken
		fetchPollinationsModels(config.pollinationsModel)

		// AI Horde
		document.getElementById('nig-horde-api-key').value = config.aiHordeApiKey
		document.getElementById('nig-horde-sampler').value = config.aiHordeSampler
		document.getElementById('nig-horde-steps').value = config.aiHordeSteps
		document.getElementById('nig-horde-cfg').value = config.aiHordeCfgScale
		document.getElementById('nig-horde-width').value = config.aiHordeWidth
		document.getElementById('nig-horde-height').value = config.aiHordeHeight
		document.getElementById('nig-horde-seed').value = config.aiHordeSeed
		document.querySelectorAll('input[name="nig-horde-post"]').forEach(cb => {
			cb.checked = config.aiHordePostProcessing.includes(cb.value)
		})
		fetchAIHordeModels(config.aiHordeModel)

		// Google
		document.getElementById('nig-google-api-key').value = config.googleApiKey
		document.getElementById('nig-model').value = config.model
		document.getElementById('nig-num-images').value = config.numberOfImages
		document.getElementById('nig-image-size').value = config.imageSize
		document.getElementById('nig-aspect-ratio').value = config.aspectRatio
		document.getElementById('nig-person-gen').value = config.personGeneration

		// OpenAI Compatible
		await loadOpenAIProfiles()
		if (config.openAICompatModelManualInput) {
			document.getElementById('nig-openai-model-container-select').style.display = 'none'
			document.getElementById('nig-openai-model-container-manual').style.display = 'block'
		} else {
			document.getElementById('nig-openai-model-container-select').style.display = 'block'
			document.getElementById('nig-openai-model-container-manual').style.display = 'none'
		}

		updateVisibleSettings()
	}

	async function populateHistoryTab() {
		const history = await getHistory()
		const historyList = document.getElementById('nig-history-list')
		historyList.innerHTML = history.length ? '' : '<li>No history yet.</li>'
		history.forEach(item => {
			const li = document.createElement('li')
			li.className = 'nig-history-item'

			const providerInfo = item.provider ? `<strong>${item.provider}</strong>` : ''
			const modelInfo = item.model ? `(${item.model})` : ''

			// Set the static part of the HTML
			li.innerHTML = `<small>${new Date(item.date).toLocaleString()} - ${providerInfo} ${modelInfo}</small>
                      <small><em>${item.prompt.substring(0, 70)}...</em></small>`

			// Create the link element separately to add the event listener
			const viewLink = document.createElement('a')
			viewLink.href = '#' // Use a non-navigating href
			viewLink.textContent = 'View Generated Image'

			viewLink.addEventListener('click', e => {
				e.preventDefault()
				if (item.url && item.url.startsWith('data:image')) {
					// For base64 images, use the internal viewer to avoid browser issues
					showImageViewer([item.url], item.prompt, item.provider)
				} else {
					// For regular URLs, open in a new tab
					window.open(item.url, '_blank')
				}
			})

			li.appendChild(viewLink)
			historyList.appendChild(li)
		})
	}

	async function cleanHistory() {
		const daysInput = document.getElementById('nig-history-clean-days')
		const days = parseInt(daysInput.value, 10)
		if (isNaN(days) || days < 1 || days > 365) {
			alert('Please enter a valid number of days (1-365).')
			return
		}
		if (confirm(`Are you sure you want to delete all history entries older than ${days} days? This cannot be undone.`)) {
			const history = await getHistory()
			const cutoffDate = Date.now() - days * 24 * 60 * 60 * 1000
			const newHistory = history.filter(item => new Date(item.date).getTime() >= cutoffDate)
			await GM_setValue('history', JSON.stringify(newHistory))
			await populateHistoryTab()
			alert(`History cleaned. ${history.length - newHistory.length} entries were removed.`)
		}
	}

	async function openConfigPanel() {
		if (!configPanel) createConfigPanel()
		configPanel.querySelectorAll('.nig-tab, .nig-tab-content').forEach(el => el.classList.remove('active'))
		configPanel.querySelector('.nig-tab[data-tab="config"]').classList.add('active')
		configPanel.querySelector('#nig-config-tab').classList.add('active')
		configPanel.querySelector('#nig-save-btn').style.display = 'block'
		await populateConfigForm()
		configPanel.style.display = 'flex'
	}

	async function saveConfig() {
		// Prompt Styling
		await setConfig('mainPromptStyle', document.getElementById('nig-main-style').value)
		await setConfig('subPromptStyle', document.getElementById('nig-sub-style').value)
		await setConfig('customStyleEnabled', document.getElementById('nig-custom-style-enable').checked)
		await setConfig('customStyleText', document.getElementById('nig-custom-style-text').value.trim())

		// AI Prompt Enhancement
		await setConfig('enhancementEnabled', document.getElementById('nig-enhancement-enabled').checked)
		await setConfig('enhancementApiKey', document.getElementById('nig-gemini-api-key').value.trim())
		await setConfig('enhancementModel', document.getElementById('nig-enhancement-model').value)
		await setConfig('enhancementTemplate', document.getElementById('nig-enhancement-template').value.trim())

		// Global Negative Prompting
		await setConfig('enableNegPrompt', document.getElementById('nig-enable-neg-prompt').checked)
		await setConfig('globalNegPrompt', document.getElementById('nig-global-neg-prompt').value.trim())

		await setConfig('selectedProvider', document.getElementById('nig-provider').value)

		// Pollinations
		await setConfig('pollinationsModel', document.getElementById('nig-pollinations-model').value)
		await setConfig('pollinationsWidth', document.getElementById('nig-pollinations-width').value)
		await setConfig('pollinationsHeight', document.getElementById('nig-pollinations-height').value)
		await setConfig('pollinationsSeed', document.getElementById('nig-pollinations-seed').value.trim())
		await setConfig('pollinationsEnhance', document.getElementById('nig-pollinations-enhance').checked)
		await setConfig('pollinationsSafe', document.getElementById('nig-pollinations-safe').checked)
		await setConfig('pollinationsNologo', document.getElementById('nig-pollinations-nologo').checked)
		await setConfig('pollinationsPrivate', document.getElementById('nig-pollinations-private').checked)
		await setConfig('pollinationsToken', document.getElementById('nig-pollinations-token').value.trim())

		// AI Horde
		await setConfig('aiHordeApiKey', document.getElementById('nig-horde-api-key').value.trim() || '0000000000')
		await setConfig('aiHordeModel', document.getElementById('nig-horde-model').value)
		await setConfig('aiHordeSampler', document.getElementById('nig-horde-sampler').value)
		await setConfig('aiHordeSteps', document.getElementById('nig-horde-steps').value)
		await setConfig('aiHordeCfgScale', document.getElementById('nig-horde-cfg').value)
		await setConfig('aiHordeWidth', document.getElementById('nig-horde-width').value)
		await setConfig('aiHordeHeight', document.getElementById('nig-horde-height').value)
		await setConfig('aiHordeSeed', document.getElementById('nig-horde-seed').value.trim())
		const postProcessing = Array.from(document.querySelectorAll('input[name="nig-horde-post"]:checked')).map(cb => cb.value)
		await setConfig('aiHordePostProcessing', postProcessing)

		// Google
		await setConfig('googleApiKey', document.getElementById('nig-google-api-key').value.trim())
		await setConfig('model', document.getElementById('nig-model').value)
		await setConfig('numberOfImages', document.getElementById('nig-num-images').value)
		await setConfig('imageSize', document.getElementById('nig-image-size').value)
		await setConfig('aspectRatio', document.getElementById('nig-aspect-ratio').value)
		await setConfig('personGeneration', document.getElementById('nig-person-gen').value)

		// OpenAI Compatible
		const baseUrl = document.getElementById('nig-openai-compat-base-url').value.trim()
		if (baseUrl) {
			const profiles = await GM_getValue('openAICompatProfiles', {})
			const manualContainer = document.getElementById('nig-openai-model-container-manual')
			const isManualMode = manualContainer.style.display !== 'none'
			let model
			if (isManualMode) {
				model = document.getElementById('nig-openai-compat-model-manual').value.trim()
			} else {
				model = document.getElementById('nig-openai-compat-model').value
			}

			profiles[baseUrl] = {
				apiKey: document.getElementById('nig-openai-compat-api-key').value.trim(),
				model: model
			}
			await setConfig('openAICompatProfiles', profiles)
			await setConfig('openAICompatActiveProfileUrl', baseUrl)
			await setConfig('openAICompatModelManualInput', isManualMode)
		}

		alert('Configuration saved!')
		// configPanel.style.display = 'none' // Panel should remain open after saving
	}

	// --- 5. INITIALIZATION ---
	async function init() {
		await updateLoggingStatus()
		createUI()
		createErrorModal()
		document.addEventListener('mouseup', handleSelection)
		document.addEventListener('selectionchange', handleSelection)
		GM_registerMenuCommand('Image Generator Settings', openConfigPanel)
	}
	init()
})()