JanitorAI Character Card Scraper

Extract character card with "T" key (WHILE IN CHAT PAGE) and save as .txt, .png, or .json (proxy required)

// ==UserScript==
// @name         JanitorAI Character Card Scraper
// @version      4.0
// @description  Extract character card with "T" key (WHILE IN CHAT PAGE) and save as .txt, .png, or .json (proxy required)
// @match        https://janitorai.com/*
// @icon         https://images.dwncdn.net/images/t_app-icon-l/p/46413ec0-e1d8-4eab-a0bc-67eadabb2604/3920235030/janitor-ai-logo
// @grant        none
// @namespace    https://sleazyfork.org/en/scripts/537206-janitorai-character-card-scraper
// @inject-into  auto
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    function runInPageContext() {

        "use strict";

        /* ============================
           ==      VARIABLES        ==
           ============================ */
        let hasInitialized = false;
        let viewActive = false;
        let shouldInterceptNext = false;
        let networkInterceptActive = false;
        let exportFormat = null;
        let chatData = null;
        let currentTab = sessionStorage.getItem("lastActiveTab") || "export";
        let useChatNameForName;
        if (localStorage.getItem("useChatNameForName") === null) {
            useChatNameForName = true; // default for first-time users
            localStorage.setItem("useChatNameForName", "true");
        } else {
            useChatNameForName = localStorage.getItem("useChatNameForName") === "true";
        }

        let applyCharToken;
        if (localStorage.getItem("applyCharToken") === null) {
            applyCharToken = false; // default for first-time users
            localStorage.setItem("applyCharToken", "false");
        } else {
            applyCharToken = localStorage.getItem("applyCharToken") !== "false";
        }
        let filenameTemplate =
            localStorage.getItem("filenameTemplateDraft") ||
            localStorage.getItem("filenameTemplate") ||
            "{name}";
        // Migration: Update old default for existing users
        const storedSavePath = localStorage.getItem("savePathTemplate");
        if (storedSavePath === "images/{creator}") {
            localStorage.setItem("savePathTemplate", "cards/{creator}");
        }

        // Simple boolean: false = show prefill, true = user has applied changes
        // Default to false for fresh installs, only true if user explicitly applied changes
        let userChangedSavePath =
            localStorage.getItem("userChangedSavePath") === "true";

        let savePathTemplate = userChangedSavePath ?
            localStorage.getItem("savePathTemplate") || "" :
            "cards/{creator}";
        let animationTimeouts = [];
        let currentActiveTab = "export"; // Track current tab state
        let restorationInProgress = false; // Prevent multiple simultaneous restorations
        let guiElement = null;
        let settingsScrollbar = null;
        let cachedUserName = "";
        let cachedProxyUrl = "";
        sessionStorage.removeItem("char_export_scroll");
        sessionStorage.removeItem("char_settings_scroll");
        const ANIMATION_DURATION = 150; // Animation duration for modal open/close in ms
        const TAB_ANIMATION_DURATION = 300; // Animation duration for tab switching in ms
        const TAB_BUTTON_DURATION = 250; // Animation duration for tab button effects
        const BUTTON_ANIMATION = 200; // Animation duration for format buttons
        const TOGGLE_ANIMATION = 350; // Animation duration for toggle switch
        const ACTIVE_TAB_COLOR = "#0080ff"; // Color for active tab indicator
        const INACTIVE_TAB_COLOR = "transparent"; // Color for inactive tab indicator
        const BUTTON_COLOR = "#3a3a3a"; // Base color for buttons
        const BUTTON_HOVER_COLOR = "#4a4a4a"; // Hover color for buttons
        const BUTTON_ACTIVE_COLOR = "#0070dd"; // Active color for buttons when clicked
        const TOOLTIP_SLIDE_FROM_RIGHT = true; // true = slide towards right (default). Set false for slide-left variant.
        const TOOLTIP_SLIDE_OFFSET = 10; // px the tooltip travels during slide animation

        const blankMeta = {
            creatorUrl: "",
            characterVersion: "",
            characterCardUrl: "",
            name: "",
            creatorNotes: "",
            personality: "",
            scenario: "",
            firstMessage: "",
            exampleDialogs: "",
            definitionExposed: false,
        };

        const characterMetaCache = {
            id: null,
            useChatNameForName: false,
            ...blankMeta,
        };

        let jaiStateObserver;

        /**
         * @param {HTMLScriptElement} scriptNode
         */
        function extractAndCacheInitialData(scriptNode) {
            if (cachedUserName && cachedProxyUrl) {
                if (jaiStateObserver) {
                    jaiStateObserver.disconnect();
                }
                return;
            }

            try {
                const match = scriptNode.textContent.match(/JSON\.parse\("([\s\S]*?)"\)/);
                if (match && match[1]) {
                    const storeState = JSON.parse(JSON.parse(`"${match[1]}"`));

                    const profile = storeState?.user?.profile;
                    if (profile?.name && !cachedUserName) {
                        cachedUserName = profile.name.trim();
                    }

                    const config = storeState?.user?.config;
                    if (config?.open_ai_reverse_proxy && !cachedProxyUrl) {
                        cachedProxyUrl = config.open_ai_reverse_proxy;
                    }

                    if (cachedUserName && cachedProxyUrl && jaiStateObserver) {
                        jaiStateObserver.disconnect();
                    }
                }
            } catch (e) {}
        }

        // STRATEGY 1: Immediate Scan
        // Look for the data right away in case it's already on the page.
        document.querySelectorAll('script').forEach(script => {
            if (script.textContent.includes('window._storeState_')) {
                extractAndCacheInitialData(script);
            }
        });

        // STRATEGY 2: Asynchronous Watcher
        // Set up an observer to catch the data if it's added to the page later.
        jaiStateObserver = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === 'SCRIPT' && node.textContent.includes('window._storeState_')) {
                        extractAndCacheInitialData(node);
                    }
                }
            }
        });

        // Start observing the entire page. The helper function will handle disconnecting.
        jaiStateObserver.observe(document.documentElement, {
            childList: true,
            subtree: true
        });

        interceptNetwork();

        /* ============================
           ==      UTILITIES        ==
           ============================ */
        function debugLog(...args) {
            if (localStorage.getItem("showDebugLogs") === "true") {
                console.log(...args);
            }
        }

        function debugWarn(...args) {
            if (localStorage.getItem("showDebugLogs") === "true") {
                console.warn(...args);
            }
        }

        function debugError(...args) {
            if (localStorage.getItem("showDebugLogs") === "true") {
                console.error(...args);
            }
        }

        function makeElement(tag, attrs = {}, styles = {}) {
            const el = document.createElement(tag);
            Object.entries(attrs).forEach(([key, value]) => (el[key] = value));

            if (styles) {
                Object.entries(styles).forEach(([key, value]) => (el.style[key] = value));
            }
            return el;
        }

        /**
         * Extract creator profile URL from fetched character page document.
         * Looks for the profile anchor used across pages.
         * @param {Document} doc – HTML document parsed from character page.
         * @returns {string} Absolute URL or empty string.
         */
        function getCreatorUrlFromDoc(doc) {
            const link = doc.querySelector("a.chakra-link.css-15sl5jl");
            if (link) {
                const href = link.getAttribute("href");
                if (href) return `https://janitorai.com${href}`;
            }
            return "";
        }

        async function saveFile(filename, blob) {
            const useFileSystemAPI =
                localStorage.getItem("useFileSystemAccess") === "true";

            if (useFileSystemAPI && "showDirectoryPicker" in window) {
                try {
                    const directoryHandle = window.selectedDirectoryHandle;

                    if (directoryHandle) {
                        const userChangedSavePath =
                            localStorage.getItem("userChangedSavePath") === "true";
                        const savePathTemplate = userChangedSavePath ?
                            localStorage.getItem("savePathTemplate") || "" :
                            "cards/{creator}";

                        let savePath = savePathTemplate;

                        debugLog("[DEBUG] Original save path template:", savePathTemplate);
                        debugLog("[DEBUG] Available chatData:", chatData);

                        const meta = await getCharacterMeta();
                        const tokens = await getFilenameTokens(meta);

                        debugLog("[DEBUG] Available tokens:", tokens);

                        Object.entries(tokens).forEach(([tokenKey, tokenValue]) => {
                            if (tokenValue && String(tokenValue).trim() !== "") {
                                const regex = new RegExp(`\\{${escapeRegExp(tokenKey)}\\}`, "g");
                                const oldSavePath = savePath;
                                savePath = savePath.replace(regex, String(tokenValue));
                                if (oldSavePath !== savePath) {
                                    debugLog(`[DEBUG] Replaced {${tokenKey}} with:`, tokenValue);
                                }
                            }
                        });

                        debugLog("[DEBUG] Final save path:", savePath);

                        savePath = savePath.replace(/^\/+|\/+$/g, "");
                        const pathSegments = savePath ?
                            savePath.split("/").filter((segment) => segment.length > 0) : [];

                        let currentDir = directoryHandle;
                        for (const segment of pathSegments) {
                            try {
                                currentDir = await currentDir.getDirectoryHandle(segment, {
                                    create: true,
                                });
                            } catch (error) {
                                debugError(
                                    `Failed to create/access directory ${segment}:`,
                                    error,
                                );
                                regularDownload(filename, blob);
                                return;
                            }
                        }

                        let finalFilename = filename;
                        let counter = 1;
                        let fileHandle;

                        while (true) {
                            try {
                                await currentDir.getFileHandle(finalFilename);
                                const lastDot = filename.lastIndexOf(".");
                                if (lastDot === -1) {
                                    finalFilename = `${filename} (${counter})`;
                                } else {
                                    const nameWithoutExt = filename.substring(0, lastDot);
                                    const extension = filename.substring(lastDot);
                                    finalFilename = `${nameWithoutExt} (${counter})${extension}`;
                                }
                                counter++;
                            } catch (error) {
                                break;
                            }
                        }

                        fileHandle = await currentDir.getFileHandle(finalFilename, {
                            create: true,
                        });
                        const writable = await fileHandle.createWritable();
                        await writable.write(blob);
                        await writable.close();

                        const successMessage = `Saved to: ${pathSegments.length > 0 ? pathSegments.join("/") + "/" : ""}${finalFilename}`;
                        debugLog(successMessage);

                        const notification = document.createElement("div");
                        notification.style.cssText = `
              position: fixed;
              top: 20px;
              right: 20px;
              background: #4CAF50;
              color: white;
              padding: 12px 20px;
              border-radius: 6px;
              z-index: 10001;
              box-shadow: 0 4px 12px rgba(0,0,0,0.3);
              font-size: 14px;
              font-weight: bold;
              opacity: 0;
              transform: translateX(100%);
              transition: all 300ms ease;
            `;
                        notification.textContent = `✓ ${successMessage}`;
                        document.body.appendChild(notification);

                        requestAnimationFrame(() => {
                            notification.style.opacity = "1";
                            notification.style.transform = "translateX(0)";
                        });

                        setTimeout(() => {
                            notification.style.opacity = "0";
                            notification.style.transform = "translateX(100%)";
                            setTimeout(() => notification.remove(), 300);
                        }, 3000);

                        return;
                    }
                } catch (error) {
                    debugError("FileSystemAccess API failed:", error);
                    const errorNotification = document.createElement("div");
                    errorNotification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #f44336;
            color: white;
            padding: 12px 20px;
            border-radius: 6px;
            z-index: 10001;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            font-size: 14px;
            font-weight: bold;
            opacity: 0;
            transform: translateX(100%);
            transition: all 300ms ease;
          `;
                    errorNotification.textContent = `⚠ FileSystem save failed, using download instead`;
                    document.body.appendChild(errorNotification);

                    requestAnimationFrame(() => {
                        errorNotification.style.opacity = "1";
                        errorNotification.style.transform = "translateX(0)";
                    });

                    setTimeout(() => {
                        errorNotification.style.opacity = "0";
                        errorNotification.style.transform = "translateX(100%)";
                        setTimeout(() => errorNotification.remove(), 300);
                    }, 3000);
                }
            }

            regularDownload(filename, blob);
        }

        function regularDownload(filename, blob) {
            const url = URL.createObjectURL(blob);
            const a = makeElement("a", {
                href: url,
                download: filename,
            });
            a.addEventListener("click", (e) => {
                e.stopPropagation();
            });
            document.body.appendChild(a);
            a.click();
            a.remove();
            URL.revokeObjectURL(url);
        }

        function getCharacterCardUrl(id) {
            return `https://janitorai.com/characters/${id}`;
        }

        function escapeRegExp(s) {
            return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
        }

        function extractTagContent(sys, charName) {
            if (!charName || !sys) return "";
            const variants = [];
            const trimmed = charName.trim();
            variants.push(trimmed);
            const collapsed = trimmed.replace(/\s+/g, " ");
            if (collapsed !== trimmed) variants.push(collapsed);
            if (trimmed.includes(" ")) variants.push(trimmed.replace(/\s+/g, "_"));

            for (const name of variants) {
                const escName = escapeRegExp(name);
                const regex = new RegExp(
                    `<${escName}(?:\\s[^>]*)?\\s*>([\\s\\S]*?)<\\/${escName}\\s*>`,
                    "i",
                );
                const m = sys.match(regex);
                if (m && m[1] != null) {
                    return m[1].trim();
                }
                const openTagRx = new RegExp(`<${escName}(?:\\s[^>]*)?\\s*>`, "i");
                const closeTagRx = new RegExp(`<\\/${escName}\\s*>`, "i");
                const openMatch = openTagRx.exec(sys);
                const closeMatch = closeTagRx.exec(sys);
                if (openMatch && closeMatch && closeMatch.index > openMatch.index) {
                    const start = openMatch.index + openMatch[0].length;
                    const end = closeMatch.index;
                    return sys.substring(start, end).trim();
                }
                try {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(
                        `<root>${sys}</root>`,
                        "application/xml",
                    );
                    const elems = doc.getElementsByTagName(name);
                    if (elems.length) {
                        return elems[0].textContent.trim();
                    }
                } catch (_) {}
            }
            return "";
        }

        function stripWatermark(text) {
            if (!text) return "";
            const lines = text.split(/\r?\n/);
            const filtered = lines.filter((l) => {
                const t = l.trim();
                return !(/^created/i.test(t) && /janitorai\.com"?$/i.test(t));
            });
            return filtered.join("\n").trim();
        }

        // === Early Chat Data Prefetch ===
        function findChatId() {
            const direct = window.location.href.match(/\/chats\/(\d+)/);
            if (direct) return direct[1];
            const scripts = document.querySelectorAll("script");
            for (const s of scripts) {
                const txt = s.textContent;
                if (!txt || !txt.includes("window._storeState_")) continue;
                const m = txt.match(/JSON\.parse\("([\s\S]*?)"\)/);
                if (m && m[1]) {
                    try {
                        const decoded = JSON.parse(`"${m[1]}"`); // unescape
                        const obj = JSON.parse(decoded);
                        let cid = null;
                        const walk = (o) => {
                            if (!o || typeof o !== "object" || cid) return;
                            if (
                                Object.prototype.hasOwnProperty.call(o, "chatId") &&
                                typeof o.chatId === "number"
                            ) {
                                cid = o.chatId;
                                return;
                            }
                            for (const k in o) walk(o[k]);
                        };
                        walk(obj);
                        if (cid) return cid;
                    } catch (_) {}
                }
            }
            return null;
        }

        function getAuthHeader() {
            const matches = document.cookie.match(/sb-auth-auth-token\.[^=]+=([^;]+)/g);
            if (matches && matches.length) {
                for (const seg of matches) {
                    const rawVal = seg.substring(seg.indexOf("=") + 1);
                    const hdr = extractBearer(rawVal);
                    if (hdr) return hdr;
                }
            }
            return null;

            function extractBearer(val) {
                let raw = decodeURIComponent(val);
                if (raw.startsWith("base64-")) raw = raw.slice(7);
                if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(raw)) {
                    debugLog("[getAuthHeader] using raw JWT");
                    return `Bearer ${raw}`;
                }
                try {
                    const json = JSON.parse(atob(raw));
                    if (json && json.access_token) {
                        debugLog("[getAuthHeader] using access_token from JSON");
                        return `Bearer ${json.access_token}`;
                    }
                } catch (err) {
                    /* ignore */
                }
                return null;
            }
            const m = document.cookie.match(/sb-auth-auth-token\.\d+=([^;]+)/);
            if (!m) return null;
            let raw = decodeURIComponent(m[1]);
            if (raw.startsWith("base64-")) raw = raw.slice(7);
            try {
                const json = JSON.parse(atob(raw));
                if (json.access_token) return `Bearer ${json.access_token}`;
            } catch (err) {
                if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(raw)) {
                    return `Bearer ${raw}`;
                }
            }
            return null;
        }

        function getAppVersion() {
            const scr = [...document.querySelectorAll("script[src]")].find((s) =>
                /\bv=([\w.-]+)/.test(s.src),
            );
            if (scr) {
                const m = scr.src.match(/\bv=([\w.-]+)/);
                if (m) return m[1];
            }
            return "2025-06-24.81a918c33";
        }

        async function prefetchChatData() {
            if (chatData) return;
            const chatId = findChatId();
            if (!chatId) return;
            const endpoint = `https://janitorai.com/hampter/chats/${chatId}`;
            const appVer = getAppVersion();
            const auth = getAuthHeader();
            debugLog("[prefetchChatData] auth:", auth);
            const baseHeaders = {
                "x-app-version": appVer,
                accept: "application/json, text/plain, */*",
            };
            if (auth) baseHeaders["Authorization"] = auth;
            debugLog("[prefetchChatData] request headers", baseHeaders);
            // First try with cookies + headers
            try {
                let res = await fetch(endpoint, {
                    method: "GET",
                    credentials: "include",
                    headers: baseHeaders,
                });
                if (res.status === 401) {
                    // Retry with Authorization header if available
                    const auth = getAuthHeader();
                    if (auth) {
                        res = await fetch(endpoint, {
                            method: "GET",
                            credentials: "include",
                            headers: {
                                ...baseHeaders,
                                Authorization: auth,
                            },
                        });
                    }
                }
                if (res.ok) {
                    const json = await res.json();
                    if (json && json.character) {
                        chatData = json;
                        debugLog("[prefetchChatData] chatData pre-fetched");
                    }
                }
            } catch (err) {
                debugWarn("[prefetchChatData] failed:", err);
            }
        }

        function tokenizeNames(text, charName, userName) {
            if (!text) return text;
            const parts = text.split("\n\n");
            const [cRx, uRx] = [charName, userName].map((n) =>
                n ? escapeRegExp(n) : "",
            );

            for (let i = 0, l = parts.length; i < l; ++i) {
                if (
                    !/^==== (Name|Chat Name|Initial Message|Character Card|Creator) ====/.test(
                        parts[i],
                    )
                ) {
                    // Always tokenize {{user}} with the corrected regex
                    if (uRx) {
                        const userRegex = new RegExp(`\\b${uRx}('s?)?(?!\\w)`, "gi");
                        parts[i] = parts[i].replace(userRegex, (match, suffix) => {
                            return `{{user}}${suffix || ""}`;
                        });
                    }

                    // Conditionally tokenize {{char}} based on the toggle
                    if (applyCharToken) {
                        if (cRx) {
                            const charRegex = new RegExp(`\\b${cRx}('s?)?(?!\\w)`, "gi");
                            parts[i] = parts[i].replace(charRegex, (match, suffix) => {
                                return `{{char}}${suffix || ""}`;
                            });
                        }
                    } else if (charName) {
                        parts[i] = parts[i].replace(/\{\{char\}\}/gi, charName);
                    }
                }
            }
            return parts.join("\n\n");
        }

        function tokenizeField(text, charName, userName) {
            if (!text) return text;
            let out = text;
            const esc = (n) => escapeRegExp(n);

            // Always apply {{user}} tokenization with the corrected regex
            if (userName) {
                const userRegex = new RegExp(`\\b${esc(userName)}('s?)?(?!\\w)`, "gi");
                out = out.replace(userRegex, (match, suffix) => `{{user}}${suffix || ""}`);
            }

            // Conditionally apply {{char}} tokenization
            if (applyCharToken && charName) {
                const charRegex = new RegExp(`\\b${esc(charName)}('s?)?(?!\\w)`, "gi");
                out = out.replace(charRegex, (match, suffix) => `{{char}}${suffix || ""}`);
            } else if (charName) {
                // De-tokenize if setting is off
                out = out.replace(/\{\{char\}\}/gi, charName);
            }

            return out;
        }

        /**
         * Adds a token value to the filename template input field
         * @param {string} token - Token to add (e.g., "id", "name", etc.)
         * @param {HTMLInputElement} inputField - The template input field
         */
        function addTokenToTemplate(token, inputField) {
            if (!inputField || !token) return;

            const currentValue = inputField.value || "";
            const cursorPos = inputField.selectionStart || currentValue.length;

            // Add space before token if there's content and no trailing space or slash
            const needsSpace =
                currentValue &&
                !currentValue.endsWith(" ") &&
                !currentValue.endsWith("/") &&
                cursorPos === currentValue.length;
            const tokenToAdd = needsSpace ? ` {${token}}` : `{${token}}`;

            const newValue =
                currentValue.slice(0, cursorPos) +
                tokenToAdd +
                currentValue.slice(cursorPos);
            inputField.value = newValue;

            // Update cursor position
            const newCursorPos = cursorPos + tokenToAdd.length;
            inputField.setSelectionRange(newCursorPos, newCursorPos);
            inputField.focus();

            // Trigger change event
            inputField.dispatchEvent(new Event("input", {
                bubbles: true
            }));
        }

        /**
         * Creates a token button for the filename template UI
         * @param {string} token - Token name
         * @param {string} label - Display label for the button
         * @param {HTMLInputElement} inputField - Template input field
         * @returns {HTMLElement} - Button element
         */
        function createTokenButton(token, label, inputElement, isSavePath = false) {
            const button = makeElement(
                "button", {
                    textContent: label,
                    type: "button",
                }, {
                    background: "#3a3a3a",
                    border: "1px solid #555",
                    color: "#fff",
                    padding: "4px 8px",
                    borderRadius: "4px",
                    cursor: "pointer",
                    fontSize: "11px",
                    transition: "all 150ms ease",
                    margin: "2px",
                },
            );

            button.addEventListener("mouseover", () => {
                button.style.background = "#4a4a4a";
                button.style.borderColor = "#666";
            });

            button.addEventListener("mouseout", () => {
                button.style.background = "#3a3a3a";
                button.style.borderColor = "#555";
            });

            button.addEventListener("mousedown", (e) => {
                // Only trigger on left mouse button
                if (e.button !== 0) return;

                e.preventDefault();
                addTokenToTemplate(token, inputElement);

                // Green flash and pulse animation for left click
                button.style.background = "#4CAF50";
                button.style.borderColor = "#66BB6A";
                button.style.transform = "scale(1.1)";
                button.style.boxShadow = "0 0 10px rgba(76, 175, 80, 0.6)";

                setTimeout(() => {
                    button.style.background = "#3a3a3a";
                    button.style.borderColor = "#555";
                    button.style.transform = "scale(1)";
                    button.style.boxShadow = "none";
                }, 100);
            });

            // Right mousedown for immediate removal
            button.addEventListener("mousedown", (e) => {
                // Only trigger on right mouse button
                if (e.button !== 2) return;

                e.preventDefault();
                e.stopPropagation();

                const currentValue = inputElement.value;
                const tokenPattern = `{${token}}`;

                if (currentValue.includes(tokenPattern)) {
                    // Find the first occurrence and remove only one instance with intelligent spacing
                    let newValue = currentValue;
                    const tokenRegex = new RegExp(`\\{${token}\\}`, "");

                    // Check if token has a space before it
                    const tokenWithSpaceRegex = new RegExp(` \\{${token}\\}`, "");

                    if (tokenWithSpaceRegex.test(newValue)) {
                        // Remove token with preceding space
                        newValue = newValue.replace(tokenWithSpaceRegex, "");
                    } else {
                        // Remove just the token
                        newValue = newValue.replace(tokenRegex, "");
                    }

                    // Clean up any double spaces that might result
                    newValue = newValue.replace(/\s+/g, " ").trim();

                    inputElement.value = newValue;

                    // Trigger input event to update state
                    if (isSavePath) {
                        savePathTemplate = newValue;
                    } else {
                        filenameTemplate = newValue;
                        localStorage.setItem("filenameTemplateDraft", filenameTemplate);
                    }

                    inputElement.dispatchEvent(new Event("input", {
                        bubbles: true
                    }));

                    // Immediate red flash and pulse animation
                    button.style.background = "#f44336";
                    button.style.borderColor = "#ff6666";
                    button.style.transform = "scale(1.1)";
                    button.style.boxShadow = "0 0 10px rgba(244, 67, 54, 0.6)";

                    setTimeout(() => {
                        button.style.background = "#3a3a3a";
                        button.style.borderColor = "#555";
                        button.style.transform = "scale(1)";
                        button.style.boxShadow = "none";
                    }, 100);
                } else {
                    // Budge animation when token not found
                    button.style.transform = "translateX(-3px)";
                    setTimeout(() => {
                        button.style.transform = "translateX(3px)";
                        setTimeout(() => {
                            button.style.transform = "translateX(0)";
                        }, 100);
                    }, 100);
                }
            });

            // Prevent context menu from appearing
            button.addEventListener("contextmenu", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });

            return button;
        }

        /**
         * Creates a standardized toggle component
         * @param {string} key - localStorage key
         * @param {string} label - Display label
         * @param {string} tooltip - Tooltip text
         * @param {boolean} defaultValue - Default checked state
         * @returns {Object} - Object containing container and input elements
         */
        function createToggle(key, label, tooltip, defaultValue = false) {
            // Get the actual value from localStorage, with defaultValue as fallback
            const storedValue = localStorage.getItem(key);
            const actualValue = storedValue !== null ? storedValue === "true" : defaultValue;

            const container = makeElement(
                "div", {}, {
                    display: "flex",
                    alignItems: "center",
                    marginBottom: "8px",
                    marginTop: "10px",
                    padding: "15px",
                    background: "#2a2a2a",
                    borderRadius: "10px",
                    gap: "12px",
                    boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                    isolation: "isolate",
                    contain: "layout style paint",
                    border: "1px solid #444",
                    background: "linear-gradient(135deg, #2a2a2a 0%, #2e2e2e 100%)",
                    transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
                    cursor: "pointer",
                    position: "relative",
                },
            );

            const wrapper = makeElement(
                "div", {
                    className: "toggle-wrapper",
                }, {
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "space-between",
                    width: "100%",
                    cursor: "pointer",
                    position: "relative",
                    isolation: "isolate",
                    transition: "all 0.2s ease",
                },
            );

            const labelElement = makeElement(
                "span", {
                    textContent: label,
                }, {
                    fontSize: "13px",
                    color: "#fff",
                    order: "2",
                    textAlign: "left",
                    flex: "1",
                    paddingLeft: "10px",
                    wordBreak: "break-word",
                    lineHeight: "1.4",
                },
            );

            const toggle = makeElement(
                "label", {
                    className: "switch",
                }, {
                    position: "relative",
                    display: "inline-block",
                    width: "40px",
                    height: "24px",
                    order: "1",
                    margin: "0",
                    flexShrink: "0",
                    borderRadius: "24px",
                    boxShadow: "0 1px 3px rgba(0,0,0,0.2) inset",
                    transition: `all ${TOGGLE_ANIMATION}ms cubic-bezier(0.4, 0, 0.2, 1)`,
                    cursor: "pointer",
                },
            );

            const slider = makeElement(
                "span", {
                    className: "slider round",
                }, {
                    position: "absolute",
                    cursor: "pointer",
                    top: "0",
                    left: "0",
                    right: "0",
                    bottom: "0",
                    backgroundColor: actualValue ? ACTIVE_TAB_COLOR : "#ccc",
                    transition: `all ${TOGGLE_ANIMATION}ms cubic-bezier(0.34, 1.56, 0.64, 1)`,
                    borderRadius: "24px",
                    overflow: "hidden",
                    boxShadow: actualValue ? "0 0 8px rgba(0, 128, 255, 0.3)" : "none",
                },
            );

            const sliderShine = makeElement(
                "div", {}, {
                    position: "absolute",
                    top: "0",
                    left: "0",
                    width: "100%",
                    height: "100%",
                    background: "linear-gradient(135deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 50%)",
                    opacity: "0.5",
                    transition: `opacity ${TOGGLE_ANIMATION}ms ease`,
                },
            );
            slider.appendChild(sliderShine);

            const sliderBefore = makeElement(
                "span", {
                    className: "slider-before",
                }, {
                    position: "absolute",
                    content: '""',
                    height: "16px",
                    width: "16px",
                    left: "4px",
                    bottom: "4px",
                    backgroundColor: "white",
                    transition: `transform ${TOGGLE_ANIMATION}ms cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow ${TOGGLE_ANIMATION}ms ease`,
                    borderRadius: "50%",
                    transform: actualValue ? "translateX(16px)" : "translateX(0)",
                    boxShadow: actualValue ?
                        "0 0 2px rgba(0,0,0,0.2), 0 0 5px rgba(0,128,255,0.3)" : "0 0 2px rgba(0,0,0,0.2)",
                },
            );

            const input = makeElement(
                "input", {
                    type: "checkbox",
                    checked: actualValue,
                    "data-key": key, // Store key for easy access
                }, {
                    opacity: "0",
                    width: "0",
                    height: "0",
                    position: "absolute",
                },
            );

            input.addEventListener("change", (e) => {
                const isChecked = e.target.checked;
                localStorage.setItem(key, isChecked);

                slider.style.backgroundColor = isChecked ? ACTIVE_TAB_COLOR : "#ccc";
                slider.style.boxShadow = isChecked ?
                    "0 0 8px rgba(0, 128, 255, 0.3)" :
                    "none";
                sliderBefore.style.transform = isChecked ?
                    "translateX(16px)" :
                    "translateX(0)";
                sliderBefore.style.boxShadow = isChecked ?
                    "0 0 2px rgba(0,0,0,0.2), 0 0 8px rgba(0,128,255,0.5)" :
                    "0 0 2px rgba(0,0,0,0.2)";

                wrapper.classList.toggle("active", isChecked);

                container.style.transform = "scale(1.02)";
                container.style.borderColor = isChecked ?
                    "rgba(0, 128, 255, 0.4)" :
                    "#444";
                container.style.boxShadow = isChecked ?
                    "0 4px 12px rgba(0, 128, 255, 0.15)" :
                    "0 2px 4px rgba(0,0,0,0.1)";

                setTimeout(() => {
                    container.style.transform = "scale(1)";
                }, 150);
            });

            slider.appendChild(sliderBefore);
            toggle.appendChild(input);
            toggle.appendChild(slider);

            // Set initial visual state based on actualValue
            if (actualValue) {
                wrapper.classList.add("active");
                container.style.borderColor = "rgba(0, 128, 255, 0.4)";
                container.style.boxShadow = "0 4px 12px rgba(0, 128, 255, 0.15)";
            }

            container.addEventListener("mouseenter", () => {
                if (!container.style.transform.includes("scale")) {
                    container.style.transform = "translateY(-1px) scale(1.005)";
                    container.style.boxShadow = "0 6px 16px rgba(0,0,0,0.15)";
                }
            });

            container.addEventListener("mouseleave", () => {
                if (!container.style.transform.includes("1.02")) {
                    container.style.transform = "translateY(0) scale(1)";
                    const isActive = input.checked;
                    container.style.boxShadow = isActive ?
                        "0 4px 12px rgba(0, 128, 255, 0.15)" :
                        "0 2px 4px rgba(0,0,0,0.1)";
                }
            });

            wrapper.addEventListener("click", (e) => {
                e.preventDefault();
                input.checked = !input.checked;
                const event = new Event("change");
                input.dispatchEvent(event);
                document.body.focus();
            });

            wrapper.appendChild(labelElement);
            labelElement.setAttribute("data-tooltip", tooltip);
            wrapper.appendChild(toggle);
            container.appendChild(wrapper);

            // Add reset icon
            const resetButton = makeElement(
                "button", {
                    innerHTML: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
                        <path d="M21 3v5h-5"/>
                        <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
                        <path d="M8 16H3v5"/>
                    </svg>`,
                    title: "Reset to default",
                }, {
                    position: "absolute",
                    right: "10px",
                    top: "50%",
                    transform: "translateY(-50%)",
                    background: "transparent",
                    border: "none",
                    color: "#666",
                    cursor: "pointer",
                    padding: "4px",
                    borderRadius: "4px",
                    transition: "all 200ms ease",
                    opacity: "0.5",
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "center",
                },
            );

            resetButton.addEventListener("mouseenter", () => {
                resetButton.style.opacity = "1";
                resetButton.style.color = "#fff";
                resetButton.style.background = "rgba(255, 255, 255, 0.1)";
            });

            resetButton.addEventListener("mouseleave", () => {
                resetButton.style.opacity = "0.5";
                resetButton.style.color = "#666";
                resetButton.style.background = "transparent";
            });

            resetButton.addEventListener("click", (e) => {
                e.stopPropagation();

                // Set to default value
                input.checked = defaultValue;
                localStorage.setItem(key, defaultValue);

                // Update visual state
                slider.style.backgroundColor = defaultValue ? ACTIVE_TAB_COLOR : "#ccc";
                slider.style.boxShadow = defaultValue ?
                    "0 0 8px rgba(0, 128, 255, 0.3)" :
                    "none";
                sliderBefore.style.transform = defaultValue ?
                    "translateX(16px)" :
                    "translateX(0)";
                sliderBefore.style.boxShadow = defaultValue ?
                    "0 0 2px rgba(0,0,0,0.2), 0 0 8px rgba(0,128,255,0.5)" :
                    "0 0 2px rgba(0,0,0,0.2)";

                wrapper.classList.toggle("active", defaultValue);

                // Flash effect
                resetButton.style.transform = "translateY(-50%) rotate(360deg)";
                resetButton.style.color = "#4CAF50";
                setTimeout(() => {
                    resetButton.style.transform = "translateY(-50%) rotate(0deg)";
                    resetButton.style.color = "#666";
                }, 400);
            });

            container.appendChild(resetButton);

            return {
                container,
                input,
                wrapper
            };
        }

        /* ============================
           ==          UI           ==
           ============================ */
        function createUI() {
            if (guiElement && document.body.contains(guiElement)) {
                return;
            }

            animationTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
            animationTimeouts = [];

            viewActive = true;

            const gui = makeElement(
                "div", {
                    id: "char-export-gui",
                }, {
                    position: "fixed",
                    top: "50%",
                    left: "50%",
                    transform: "translate(-50%, -50%) scale(0.95)",
                    background: "#222",
                    color: "white",
                    padding: "15px 20px 7px",
                    borderRadius: "8px",
                    boxShadow: "0 0 20px rgba(0,0,0,0.5)",
                    zIndex: "10000",
                    textAlign: "center",
                    width: "min(480px, 90vw)",
                    minHeight: "400px",
                    maxHeight: "85vh",
                    overflow: "hidden",
                    display: "flex",
                    flexDirection: "column",
                    boxSizing: "border-box",
                    opacity: "0",
                    transition: `opacity ${ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1), transform ${ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1)`,
                },
            );

            guiElement = gui;
            const unselectStyle = document.createElement("style");
            unselectStyle.textContent = `#char-export-gui, #char-export-gui * {
    user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none;
  }
  #char-export-gui {
    backdrop-filter: blur(6px);
    background: rgba(34,34,34,0.92);
    border: none;
    border-radius: 8px;
  }
  #char-export-overlay {
    position: fixed; top: 0; left: 0; width: 100%; height: 100%;
    background: rgba(0,0,0,0.5); z-index: 9998;
  }
  #char-export-gui #char-export-tooltip {
    font-size: 12px; background: #222; color: #fff;
    padding: 6px 10px; border-radius: 4px; pointer-events: none;
  }
  #char-export-gui button {
    background: #333; color: #fff; border: none;
    border-radius: 4px; padding: 8px 12px;
    transition: background 200ms ease, transform 200ms ease;
  }
  #char-export-gui button:hover:not(:disabled) {
    background: #444; transform: translateY(-1px);
  }
  #char-export-gui button:disabled {
    opacity: 0.6; cursor: not-allowed;
  }
  #char-export-gui button:focus {
    outline: none;
  }

  /* Enhanced input field styling */
  #char-export-gui input[type="text"] {
    transition: border-color 300ms cubic-bezier(0.4, 0, 0.2, 1) !important;
    border-radius: 6px !important;
  }

  #char-export-gui input[type="text"]:not(.applying-changes):hover {
    border-color: #777 !important;
  }

  #char-export-gui input[type="text"]:not(.applying-changes):focus {
    border-color: #888 !important;
  }

  /* Ensure apply animation takes priority */
  #char-export-gui input.applying-changes {
    border-color: #4CAF50 !important;
  }

  /* Disable all browser tooltips */
  #char-export-gui input,
  #char-export-gui input:hover,
  #char-export-gui input:focus,
  #char-export-gui textarea,
  #char-export-gui textarea:hover,
  #char-export-gui textarea:focus {
    title: "" !important;
  }

  #char-export-gui input[title],
  #char-export-gui textarea[title] {
    title: "" !important;
  }

/* Enhanced toggle container styling */
.toggle-wrapper {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  border-radius: 8px !important;
}

.toggle-wrapper:hover {
  transform: translateY(-1px) scale(1.005) !important;
  box-shadow: 0 6px 16px rgba(0,0,0,0.15) !important;
  background: linear-gradient(135deg, rgba(128, 128, 128, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%) !important;
}

.toggle-wrapper.active {
  border-color: rgba(0, 128, 255, 0.4) !important;
  box-shadow: 0 4px 12px rgba(0, 128, 255, 0.15) !important;
}

  /* Token button styling */
  #char-export-gui button[type="button"] {
    transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1) !important;
  }

  #char-export-gui button[type="button"]:hover {
    transform: translateY(-2px) scale(1.05) !important;
    box-shadow: 0 4px 12px rgba(0,0,0,0.2) !important;
  }

  /* Directory button styling */
  .directory-button {
    transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1) !important;
  }

  .directory-button:hover {
    box-shadow: 0 0 8px rgba(74, 144, 226, 0.4) !important;
    background: #4a90e2 !important;
  }

  .directory-button:active {
    transform: scale(0.98) !important;
  }

  /* Custom overlay scrollbar - hide native scrollbar completely */
  #settings-tab {
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE/Edge */
    margin-right: -20px;
    padding-right: 20px;
  }

  #settings-tab::-webkit-scrollbar {
    display: none; /* Chrome/Safari */
  }

  /* Custom scrollbar overlay container */
  .custom-scrollbar-container {
    position: relative;
  }

  .custom-scrollbar-track {
    position: absolute;
    top: 0;
    right: -6px;
    width: 4px;
    height: 100%;
    background: rgba(68, 68, 68, 0.1);
    border-radius: 2px;
    opacity: 0.6;
    visibility: visible;
    transition: opacity 50ms ease, box-shadow 100ms ease;
    z-index: 1000;
    pointer-events: auto;
  }

  .custom-scrollbar-track.visible {
    opacity: 0.6;
    visibility: visible;
  }

  .custom-scrollbar-track:hover {
    opacity: 1;
    background: rgba(68, 68, 68, 0.2);
    box-shadow: 0 0 2px rgba(136, 136, 136, 0.2);
  }

  .custom-scrollbar-track.expanded {
    opacity: 1;
    background: rgba(68, 68, 68, 0.2);
    box-shadow: 0 0 4px rgba(136, 136, 136, 0.3);
  }

  .custom-scrollbar-thumb {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    background: rgba(136, 136, 136, 0.7);
    border-radius: 2px;
    border: none;
    transition: background 50ms ease;
    cursor: pointer;
    min-height: 20px;
  }

  .custom-scrollbar-thumb:hover {
    background: rgba(170, 170, 170, 0.8);
  }

  .custom-scrollbar-thumb:active,
  .custom-scrollbar-thumb.dragging {
    background: rgba(200, 200, 200, 0.9);
  }
  }`;
            document.head.appendChild(unselectStyle);

            const tabContainer = makeElement(
                "div", {}, {
                    display: "flex",
                    justifyContent: "center",
                    marginBottom: "20px",
                    borderBottom: "1px solid #444",
                    paddingBottom: "12px",
                    width: "100%",
                },
            );

            const tabsWrapper = makeElement(
                "div", {}, {
                    display: "flex",
                    justifyContent: "center",
                    width: "100%",
                    maxWidth: "min(400px, 100%)",
                    margin: "0 auto",
                },
            );

            const createTabButton = (text, isActive) => {
                const button = makeElement(
                    "button", {
                        textContent: text,
                    }, {
                        background: "transparent",
                        border: "none",
                        color: "#fff",
                        padding: "8px 20px",
                        cursor: "pointer",
                        margin: "0 5px",
                        fontWeight: "bold",
                        flex: "1",
                        textAlign: "center",
                        position: "relative",
                        overflow: "hidden",
                        transition: `opacity ${TAB_BUTTON_DURATION}ms ease, transform ${TAB_BUTTON_DURATION}ms ease, color ${TAB_BUTTON_DURATION}ms ease`,
                    },
                );

                const indicator = makeElement(
                    "div", {}, {
                        position: "absolute",
                        bottom: "0",
                        left: "0",
                        width: "100%",
                        height: "2px",
                        background: isActive ? ACTIVE_TAB_COLOR : INACTIVE_TAB_COLOR,
                        transition: `transform ${TAB_BUTTON_DURATION}ms ease, background-color ${TAB_BUTTON_DURATION}ms ease`,
                    },
                );

                if (!isActive) {
                    button.style.opacity = "0.7";
                    indicator.style.transform = "scaleX(0.5)";
                }

                button.appendChild(indicator);
                return {
                    button,
                    indicator,
                };
            };

            const {
                button: exportTab,
                indicator: exportIndicator
            } = createTabButton(
                "Export",
                true,
            );
            const {
                button: settingsTab,
                indicator: settingsIndicator
            } =
            createTabButton("Settings", false);

            exportTab.onmouseover = () => {
                if (currentTab !== "export") {
                    exportTab.style.opacity = "1";
                    exportTab.style.transform = "translateY(-2px)";
                    exportIndicator.style.transform = "scaleX(0.8)";
                }
            };
            exportTab.onmouseout = () => {
                if (currentTab !== "export") {
                    exportTab.style.opacity = "0.7";
                    exportTab.style.transform = "";
                    exportIndicator.style.transform = "scaleX(0.5)";
                }
            };

            settingsTab.onmouseover = () => {
                if (currentTab !== "settings") {
                    settingsTab.style.opacity = "1";
                    settingsTab.style.transform = "translateY(-2px)";
                    settingsIndicator.style.transform = "scaleX(0.8)";
                }
            };
            settingsTab.onmouseout = () => {
                if (currentTab !== "settings") {
                    settingsTab.style.opacity = "0.7";
                    settingsTab.style.transform = "";
                    settingsIndicator.style.transform = "scaleX(0.5)";
                }
            };

            tabsWrapper.appendChild(exportTab);
            tabsWrapper.appendChild(settingsTab);
            tabContainer.appendChild(tabsWrapper);
            gui.appendChild(tabContainer);
            /* ========= Dynamic Tooltip ========= */
            (() => {
                let tEl;
                const show = (target) => {
                    const msg = target.getAttribute("data-tooltip");
                    if (!msg) return;
                    if (!tEl) {
                        tEl = document.createElement("div");
                        tEl.id = "char-export-tooltip";
                        tEl.id = "char-export-tooltip";
                        tEl.style.cssText =
                            "position:fixed;padding:6px 10px;font-size:12px;background:#222;color:#fff;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 4px 12px rgba(0,0,0,0.4);opacity:0;transition:opacity 200ms ease,transform 200ms ease;z-index:10002;";
                        const offset = TOOLTIP_SLIDE_FROM_RIGHT ?
                            -TOOLTIP_SLIDE_OFFSET :
                            TOOLTIP_SLIDE_OFFSET;
                        tEl.style.transform = `translateX(${offset}px)`;
                        document.body.appendChild(tEl);
                    }
                    tEl.textContent = msg;
                    const guiRect = gui.getBoundingClientRect();
                    const tgtRect = target.getBoundingClientRect();
                    tEl.style.top =
                        tgtRect.top + tgtRect.height / 2 - tEl.offsetHeight / 2 + "px";
                    tEl.style.left = guiRect.right + 10 + "px";
                    requestAnimationFrame(() => {
                        tEl.style.opacity = "1";
                        tEl.style.transform = "translateX(0)";
                    });
                };
                const hide = () => {
                    if (!tEl) return;
                    tEl.style.opacity = "0";
                    const offsetHide = TOOLTIP_SLIDE_FROM_RIGHT ?
                        -TOOLTIP_SLIDE_OFFSET :
                        TOOLTIP_SLIDE_OFFSET;
                    tEl.style.transform = `translateX(${offsetHide}px)`;
                };
                gui.addEventListener("mouseover", (e) => {
                    const tgt = e.target.closest("[data-tooltip]");
                    if (tgt) show(tgt);
                });
                gui.addEventListener("mouseout", (e) => {
                    const tgt = e.target.closest("[data-tooltip]");
                    if (tgt) hide();
                });
                window.addEventListener("keydown", (ev) => {
                    hide();
                    if ((ev.key === "t" || ev.key === "T") && tEl) {
                        tEl.style.opacity = "0";
                        const offsetHide = TOOLTIP_SLIDE_FROM_RIGHT ?
                            -TOOLTIP_SLIDE_OFFSET :
                            TOOLTIP_SLIDE_OFFSET;
                        tEl.style.transform = `translateX(${offsetHide}px)`;
                    }
                });
            })();

            const exportContent = makeElement(
                "div", {
                    id: "export-tab",
                }, {
                    maxHeight: "60vh",
                    overflowY: "auto",
                    padding: "0 5px 10px 0",
                    display: "flex",
                    flexDirection: "column",
                    justifyContent: "center",
                },
            );

            const title = makeElement(
                "h1", {
                    textContent: "Export Character Card",
                }, {
                    margin: "-10px 0 25px 0",
                    fontSize: "24px",
                    paddingTop: "0px",
                    textAlign: "center",
                    fontWeight: "bold",
                    color: "#fff",
                    borderBottom: "2px solid #444",
                    paddingBottom: "8px",
                },
            );
            exportContent.appendChild(title);

            const buttonContainer = makeElement(
                "div", {}, {
                    display: "flex",
                    gap: "12px",
                    justifyContent: "center",
                    marginBottom: "8px",
                    marginTop: "12px",
                },
            );

            ["TXT", "PNG", "JSON"].forEach((format) => {
                const type = format.toLowerCase();

                const button = makeElement(
                    "button", {
                        textContent: format,
                    }, {
                        background: BUTTON_COLOR,
                        border: "none",
                        color: "white",
                        padding: "12px 24px",
                        borderRadius: "8px",
                        cursor: "pointer",
                        fontWeight: "bold",
                        position: "relative",
                        overflow: "hidden",
                        flex: "1",
                        maxWidth: "min(120px, 30%)",
                        transition: `all ${BUTTON_ANIMATION}ms ease`,
                        boxShadow: "0 3px 6px rgba(0,0,0,0.15)",
                        transform: "translateY(0)",
                        fontSize: "14px",
                    },
                );

                const shine = makeElement(
                    "div", {}, {
                        position: "absolute",
                        top: "0",
                        left: "0",
                        width: "100%",
                        height: "100%",
                        background: "linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 60%)",
                        transform: "translateX(-100%)",
                        transition: `transform ${BUTTON_ANIMATION * 1.5}ms ease-out`,
                        pointerEvents: "none",
                    },
                );
                button.appendChild(shine);

                button.onmouseover = () => {
                    button.style.background = BUTTON_HOVER_COLOR;
                    button.style.transform = "translateY(-3px) scale(1.02)";
                    button.style.boxShadow = "0 6px 20px rgba(0,0,0,0.25)";
                    button.style.filter = "brightness(1.1)";
                    shine.style.transform = "translateX(100%)";
                };

                button.onmouseout = () => {
                    button.style.background = BUTTON_COLOR;
                    button.style.transform = "translateY(0) scale(1)";
                    button.style.boxShadow = "0 3px 6px rgba(0,0,0,0.15)";
                    button.style.filter = "brightness(1)";
                    shine.style.transform = "translateX(-100%)";
                };

                button.onmousedown = () => {
                    button.style.transform = "translateY(0) scale(0.98)";
                    button.style.boxShadow = "0 2px 4px rgba(0,0,0,0.3)";
                    button.style.background = BUTTON_ACTIVE_COLOR;
                    button.style.filter = "brightness(1.2)";
                };

                button.onmouseup = () => {
                    button.style.transform = "translateY(-3px) scale(1.02)";
                    button.style.boxShadow = "0 6px 20px rgba(0,0,0,0.25)";
                    button.style.background = BUTTON_HOVER_COLOR;
                    button.style.filter = "brightness(1.1)";
                };

                button.onclick = (e) => {
                    const rect = button.getBoundingClientRect();
                    const x = e.clientX - rect.left;
                    const y = e.clientY - rect.top;

                    const ripple = makeElement(
                        "div", {}, {
                            position: "absolute",
                            borderRadius: "50%",
                            backgroundColor: "rgba(255,255,255,0.4)",
                            width: "5px",
                            height: "5px",
                            transform: "scale(1)",
                            opacity: "1",
                            animation: "ripple 600ms linear",
                            pointerEvents: "none",
                            top: `${y}px`,
                            left: `${x}px`,
                            marginLeft: "-2.5px",
                            marginTop: "-2.5px",
                        },
                    );

                    button.appendChild(ripple);

                    exportFormat = type;
                    closeV();
                    extraction();

                    setTimeout(() => ripple.remove(), 600);
                };

                buttonContainer.appendChild(button);
            });

            if (!document.getElementById("char-export-style")) {
                const style = document.createElement("style");
                style.id = "char-export-style";
                style.textContent = `
            @keyframes ripple {
              to {
                transform: scale(30);
                opacity: 0; pointer-events: none;
              }
            }
          `;
                document.head.appendChild(style);
            }

            if (!document.getElementById("char-export-responsive")) {
                const responsiveStyle = document.createElement("style");
                responsiveStyle.id = "char-export-responsive";
                responsiveStyle.textContent = `
      @media (max-width: 600px) {
        #char-export-gui {
          width: 95vw !important;
          height: 90vh !important;
          max-height: 90vh !important;
          padding: 10px 15px 5px !important;
        }

        #content-wrapper {
          min-height: 280px !important;
        }

        #export-tab, #settings-tab {
          padding: 10px !important;
        }

        .toggle-wrapper {
          flex-direction: column;
          align-items: flex-start !important;
          gap: 8px !important;
        }

        .toggle-wrapper span {
          order: 1 !important;
          padding-left: 0 !important;
          text-align: left !important;
        }

        .toggle-wrapper label {
          order: 2 !important;
          align-self: flex-end;
        }
      }

      @media (max-height: 600px) {
        #char-export-gui {
          max-height: 95vh !important;
        }
      }
        `;
                document.head.appendChild(responsiveStyle);
            }

            if (!document.getElementById("char-export-settings-scroll")) {
                const settingsScrollStyle = document.createElement("style");
                settingsScrollStyle.id = "char-export-settings-scroll";
                settingsScrollStyle.textContent = `
      #settings-tab {
        scrollbar-width: thin;
        scrollbar-color: #555 #2a2a2a;
      }
      #settings-tab::-webkit-scrollbar {
        width: 6px;
      }
      #settings-tab::-webkit-scrollbar-track {
        background: #2a2a2a;
        border-radius: 3px;
      }
      #settings-tab::-webkit-scrollbar-thumb {
        background: #555;
        border-radius: 3px;
      }
      #settings-tab::-webkit-scrollbar-thumb:hover {
        background: #666;
      }

      /* Enhanced hover effects */
      .toggle-wrapper {
        transition: background 200ms ease;
      }
      .toggle-wrapper:hover {
        background: rgba(255, 255, 255, 0.02);
      }

      /* Enhanced section hover effects */
      #settings-tab > div {
        transition: box-shadow 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                    border 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                    transform 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
        border: 1px solid transparent;
        background: #2a2a2a;
      }
      #settings-tab > div:hover {
        box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2),
                    0 4px 12px rgba(0,0,0,0.15);
        border: 1px solid rgba(128, 128, 128, 0.3);
        transform: translateY(-2px) scale(1.01);
        background: linear-gradient(135deg, #2a2a2a 0%, #323232 100%);
      }

      /* Enhanced toggle hover effects */
      .toggle-wrapper {
        transition: background 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
                    box-shadow 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
        border-radius: 8px;
        contain: layout style;
        isolation: isolate;
      }
      .toggle-wrapper:hover {
        background: linear-gradient(135deg, rgba(128, 128, 128, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
        box-shadow: inset 0 0 0 1px rgba(128, 128, 128, 0.2);
      }

      /* Disable all tooltips inside UI */
      #char-export-gui [data-tooltip]:hover::before,
      #char-export-gui [data-tooltip]:hover::after,
      #char-export-gui input:hover::before,
      #char-export-gui input:hover::after,
      #char-export-gui input[data-tooltip-disabled]:hover::before,
      #char-export-gui input[data-tooltip-disabled]:hover::after {
        display: none !important;
        opacity: 0 !important;
      }

      /* Slot machine animation overflow clipping */
      .template-input-container {
        overflow: hidden !important;
        position: relative !important;
      }

      .template-apply-btn {
        will-change: transform, opacity, filter;
      }

      /* Applied text animation clipping */
      .template-input-container span {
        will-change: transform, opacity, filter;
        user-select: none;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
      }

      /* Disable browser tooltips on input */
      #char-export-gui input[data-tooltip-disabled] {
        pointer-events: auto !important;
      }
      #char-export-gui input[data-tooltip-disabled]:hover::before,
      #char-export-gui input[data-tooltip-disabled]:hover::after {
        display: none !important;
      }
    `;
                document.head.appendChild(settingsScrollStyle);
            }

            if (!document.getElementById("char-export-tooltip-style")) {
                const tooltipStyle = document.createElement("style");
                tooltipStyle.id = "char-export-tooltip-style";
                tooltipStyle.textContent = `
  [data-tooltip] {
    position: relative;
  }
  [data-tooltip]::before {
    content: '';
    position: absolute;
    left: calc(100% + 4px);
    top: 50%;
    transform: translateY(-50%) scaleY(0.5);
    border: solid transparent;
    border-width: 6px 6px 6px 0;
    border-left-color: #333;
    opacity: 0; pointer-events: none;
    transition: opacity 150ms ease;
    z-index: 10001;
  }
  [data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%) scale(0.8);
    background: #333;
    color: #fff;
    padding: 6px 10px;
    border-radius: 4px;
    white-space: nowrap;
    opacity: 0; pointer-events: none;
    transition: opacity 150ms ease, transform 150ms ease;
    z-index: 10001;
  }
  [data-tooltip]:hover::before,
  [data-tooltip]:hover::after {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }

  .toggle-wrapper {
    border-radius: 8px;
    transition: background 0.2s ease, box-shadow 0.2s ease;
  }
  .toggle-wrapper.active {
    background: transparent;
    box-shadow: none;
  }
              `;
                document.head.appendChild(tooltipStyle);
                if (!document.getElementById("char-export-scrollbar-style")) {
                    const scrollStyle = document.createElement("style");
                    scrollStyle.id = "char-export-scrollbar-style";
                    scrollStyle.textContent = `
  #content-wrapper { overflow: visible; }
  #settings-tab { overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; }
  #settings-tab::-webkit-scrollbar { width: 0; height: 0; }
  `;
                    document.head.appendChild(scrollStyle);
                    if (!document.getElementById("char-export-tooltip-override-style")) {
                        const overrideStyle = document.createElement("style");
                        overrideStyle.id = "char-export-tooltip-override-style";
                        overrideStyle.textContent = `
  [data-tooltip]::before { transform: translateY(-50%) translateX(-6px); transition: transform 200ms ease, opacity 200ms ease; }
  [data-tooltip]::after { transform: translateY(-50%) translateX(-10px); transition: transform 200ms ease, opacity 200ms ease; }
  [data-tooltip]:hover::before { transform: translateY(-50%) translateX(0); opacity:1; }
  [data-tooltip]:hover::after { transform: translateY(-50%) translateX(0); opacity:1; }

[data-tooltip]::before,[data-tooltip]::after{display:none !important;}
@keyframes toggle-pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }
.toggle-wrapper.pulse { animation: toggle-pulse 300ms ease; }

.switch .slider-before { transition: transform 200ms ease, box-shadow 200ms ease; }
.toggle-wrapper.active .slider-before { box-shadow: 0 0 2px rgba(0,0,0,0.2), 0 0 8px rgba(0,128,255,0.5); }
.toggle-wrapper.active .slider { background-color: #0080ff; }
              `;
                        document.head.appendChild(overrideStyle);
                    }
                }
            }

            exportContent.appendChild(buttonContainer);

            // Character Preview Section (for showdefinition=true)
            // Replace the existing character preview section in your userscript with this updated version

            const previewSection = makeElement(
                "div", {
                    id: "character-preview",
                }, {
                    marginTop: "20px",
                    padding: "20px",
                    background: "linear-gradient(135deg, #2a2a2a 0%, #2e2e2e 100%)",
                    borderRadius: "10px",
                    boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
                    border: "1px solid #444",
                    display: "block",
                    opacity: "1",
                    transform: "translateY(0)",
                    transition: "all 300ms ease",
                },
            );

            const previewTitle = makeElement(
                "h3", {
                    textContent: "CHARACTER PREVIEW",
                }, {
                    margin: "0 0 20px 0",
                    fontSize: "18px",
                    color: "#fff",
                    fontWeight: "bold",
                    textAlign: "center",
                    textTransform: "uppercase",
                    letterSpacing: "2px",
                    borderBottom: "2px solid #0080ff",
                    paddingBottom: "10px",
                },
            );
            previewSection.appendChild(previewTitle);

            // Character Info Header
            const characterHeader = makeElement(
                "div", {}, {
                    display: "flex",
                    alignItems: "flex-start",
                    gap: "20px",
                    marginBottom: "25px",
                    padding: "15px",
                    background: "rgba(0, 128, 255, 0.1)",
                    borderRadius: "8px",
                    border: "1px solid rgba(0, 128, 255, 0.3)",
                },
            );

            // Avatar Section
            const avatarFrame = makeElement(
                "div", {}, {
                    width: "80px",
                    height: "80px",
                    borderRadius: "12px",
                    overflow: "hidden",
                    background: "linear-gradient(145deg, #333, #1a1a1a)",
                    border: "2px solid #555",
                    boxShadow: "0 4px 12px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.1)",
                    position: "relative",
                    flexShrink: "0",
                },
            );

            const avatarImg = makeElement(
                "img", {
                    id: "preview-avatar",
                    alt: "Character Avatar",
                }, {
                    width: "100%",
                    height: "100%",
                    objectFit: "cover",
                    display: "block",
                },
            );

            avatarFrame.appendChild(avatarImg);

            // Character Info
            const characterInfo = makeElement(
                "div", {}, {
                    flex: "1",
                    minWidth: "0",
                },
            );

            const characterName = makeElement(
                "h4", {
                    id: "preview-character-name",
                }, {
                    margin: "0 0 8px 0",
                    fontSize: "20px",
                    color: "#fff",
                    fontWeight: "bold",
                    wordBreak: "break-word",
                },
            );

            const characterChatName = makeElement(
                "div", {
                    id: "preview-chat-name",
                }, {
                    fontSize: "14px",
                    color: "#4CAF50",
                    marginBottom: "8px",
                    display: "none",
                },
            );

            const characterCreator = makeElement(
                "div", {
                    id: "preview-creator-info",
                }, {
                    fontSize: "13px",
                    color: "#888",
                    marginBottom: "5px",
                },
            );

            const characterTokens = makeElement(
                "div", {
                    id: "preview-tokens",
                }, {
                    fontSize: "12px",
                    color: "#4CAF50",
                    fontWeight: "bold",
                },
            );

            characterInfo.appendChild(characterName);
            characterInfo.appendChild(characterChatName);
            characterInfo.appendChild(characterCreator);
            characterInfo.appendChild(characterTokens);

            characterHeader.appendChild(avatarFrame);
            characterHeader.appendChild(characterInfo);
            previewSection.appendChild(characterHeader);

            // Collapsible sections container
            const sectionsContainer = makeElement(
                "div", {
                    id: "collapsible-sections",
                }, {
                    display: "flex",
                    flexDirection: "column",
                    gap: "15px",
                },
            );

            // Custom SVG Icons
            const customIcons = {
                description: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
                                <polyline points="14,2 14,8 20,8"/>
                                <line x1="16" y1="13" x2="8" y2="13"/>
                                <line x1="16" y1="17" x2="8" y2="17"/>
                                <polyline points="10,9 9,9 8,9"/>
                            </svg>`,
                scenario: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
                                <path d="M12 8v4l-2-1"/>
                            </svg>`,
                firstMessage: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
                                <path d="M8 10h8"/>
                                <path d="M8 14h4"/>
                            </svg>`,
                examples: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
                                <path d="M13 8H7"/>
                                <path d="M17 12H7"/>
                                <circle cx="17" cy="8" r="1"/>
                            </svg>`
            };

            // Dynamic theme variables with enhanced color palette
            let currentTheme = {
                primary: '#0080ff',
                secondary: '#4CAF50',
                accent: '#ff6b6b',
                tertiary: '#9c27b0',
                quaternary: '#ff9800',
                background: 'linear-gradient(135deg, #2a2a2a 0%, #2e2e2e 100%)',
                headerBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)',
                panelBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)'
            };

            // Enhanced color analysis function using ColorThief
            // Lightweight color extraction with CORS bypass
            function analyzeImageColors(imageElement) {
                return new Promise((resolve) => {
                    try {
                        // Fetch the image as a blob to bypass CORS
                        fetch(imageElement.src)
                            .then(response => response.blob())
                            .then(blob => {
                                const blobUrl = URL.createObjectURL(blob);
                                extractColorsFromBlob(blobUrl, resolve);
                            })
                            .catch(error => {
                                console.warn('Failed to fetch image:', error);
                                resolve(getDefaultTheme());
                            });
                    } catch (error) {
                        console.warn('Color extraction setup failed:', error);
                        resolve(getDefaultTheme());
                    }
                });
            }

            function extractColorsFromBlob(blobUrl, resolve) {
                try {
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    const img = new Image();

                    // Set canvas size (smaller for performance)
                    canvas.width = 150;
                    canvas.height = 150;

                    img.onload = () => {
                        try {
                            // Draw image to canvas
                            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                            // Get image data
                            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                            const data = imageData.data;

                            // Color quantization - group similar colors
                            const colorMap = new Map();
                            const step = 4; // Sample every pixel

                            for (let i = 0; i < data.length; i += step * 4) {
                                const r = data[i];
                                const g = data[i + 1];
                                const b = data[i + 2];
                                const alpha = data[i + 3];

                                // Skip transparent pixels
                                if (alpha < 128) continue;

                                // Quantize colors to reduce similar shades
                                const qr = Math.floor(r / 16) * 16;
                                const qg = Math.floor(g / 16) * 16;
                                const qb = Math.floor(b / 16) * 16;

                                const key = `${qr},${qg},${qb}`;

                                if (colorMap.has(key)) {
                                    const existing = colorMap.get(key);
                                    existing.count++;
                                    existing.totalR += r;
                                    existing.totalG += g;
                                    existing.totalB += b;
                                } else {
                                    colorMap.set(key, {
                                        count: 1,
                                        totalR: r,
                                        totalG: g,
                                        totalB: b
                                    });
                                }
                            }

                            // Convert to array and calculate averages
                            const colors = Array.from(colorMap.values())
                                .map(color => ({
                                    r: Math.round(color.totalR / color.count),
                                    g: Math.round(color.totalG / color.count),
                                    b: Math.round(color.totalB / color.count),
                                    count: color.count
                                }))
                                .filter(color => {
                                    // Filter out very dark, very light, or very gray colors
                                    const brightness = (color.r + color.g + color.b) / 3;
                                    const saturation = Math.abs(Math.max(color.r, color.g, color.b) - Math.min(color.r, color.g, color.b));
                                    return brightness > 30 && brightness < 220 && saturation > 25;
                                })
                                .sort((a, b) => b.count - a.count) // Sort by frequency
                                .slice(0, 8); // Take top 8 colors

                            if (colors.length === 0) {
                                // If no colors pass the filter, use less strict filtering
                                const fallbackColors = Array.from(colorMap.values())
                                    .map(color => ({
                                        r: Math.round(color.totalR / color.count),
                                        g: Math.round(color.totalG / color.count),
                                        b: Math.round(color.totalB / color.count),
                                        count: color.count
                                    }))
                                    .filter(color => {
                                        const brightness = (color.r + color.g + color.b) / 3;
                                        return brightness > 20 && brightness < 235;
                                    })
                                    .sort((a, b) => b.count - a.count)
                                    .slice(0, 6);

                                resolve(generateThemeFromColors(fallbackColors));
                            } else {
                                resolve(generateThemeFromColors(colors));
                            }

                        } catch (error) {
                            console.warn('Color extraction failed:', error);
                            resolve(getDefaultTheme());
                        } finally {
                            // Clean up blob URL
                            URL.revokeObjectURL(blobUrl);
                        }
                    };

                    img.onerror = () => {
                        console.warn('Image loading failed for blob URL');
                        resolve(getDefaultTheme());
                        URL.revokeObjectURL(blobUrl);
                    };

                    // Load the blob URL
                    img.src = blobUrl;

                } catch (error) {
                    console.warn('Blob color extraction failed:', error);
                    resolve(getDefaultTheme());
                    URL.revokeObjectURL(blobUrl);
                }
            }

            function generateThemeFromColors(colors) {
                if (!colors || colors.length === 0) {
                    return getDefaultTheme();
                }

                // Convert RGB to hex
                const toHex = (r, g, b) => `#${[r, g, b].map(x => Math.round(x).toString(16).padStart(2, '0')).join('')}`;

                // Create gradient from multiple colors
                const createGradient = (colorArray, direction = '90deg') => {
                    if (colorArray.length === 1) {
                        return toHex(colorArray[0].r, colorArray[0].g, colorArray[0].b);
                    }

                    const colorStops = colorArray.slice(0, 6).map((color, index) => {
                        const percentage = (index / Math.max(1, colorArray.length - 1)) * 100;
                        return `${toHex(color.r, color.g, color.b)} ${percentage}%`;
                    }).join(', ');

                    return `linear-gradient(${direction}, ${colorStops})`;
                };

                // Calculate color vibrancy (saturation * brightness)
                const calculateVibrancy = (color) => {
                    const max = Math.max(color.r, color.g, color.b);
                    const min = Math.min(color.r, color.g, color.b);
                    const saturation = max === 0 ? 0 : (max - min) / max;
                    const brightness = (color.r + color.g + color.b) / (3 * 255);
                    return saturation * brightness;
                };

                // Sort colors by vibrancy for better theme selection
                const sortedColors = colors.slice().sort((a, b) => {
                    const vibrancyA = calculateVibrancy(a);
                    const vibrancyB = calculateVibrancy(b);
                    return vibrancyB - vibrancyA;
                });

                // Select diverse colors for theme roles
                const themeColors = [];

                // Primary: Most vibrant color
                if (sortedColors[0]) {
                    themeColors.push(toHex(sortedColors[0].r, sortedColors[0].g, sortedColors[0].b));
                }

                // Secondary: Find a contrasting color
                let secondaryIndex = 1;
                if (sortedColors.length > 2) {
                    for (let i = 1; i < Math.min(sortedColors.length, 4); i++) {
                        const colorDiff = Math.abs(sortedColors[0].r - sortedColors[i].r) +
                            Math.abs(sortedColors[0].g - sortedColors[i].g) +
                            Math.abs(sortedColors[0].b - sortedColors[i].b);
                        if (colorDiff > 80) {
                            secondaryIndex = i;
                            break;
                        }
                    }
                }
                if (sortedColors[secondaryIndex]) {
                    themeColors.push(toHex(sortedColors[secondaryIndex].r, sortedColors[secondaryIndex].g, sortedColors[secondaryIndex].b));
                }

                // Add remaining colors for accent, tertiary, quaternary
                for (let i = 0; i < sortedColors.length && themeColors.length < 5; i++) {
                    const hex = toHex(sortedColors[i].r, sortedColors[i].g, sortedColors[i].b);
                    if (!themeColors.includes(hex)) {
                        themeColors.push(hex);
                    }
                }

                // Fill with defaults if needed
                const defaults = ['#0080ff', '#4CAF50', '#ff6b6b', '#9c27b0', '#ff9800'];
                while (themeColors.length < 5) {
                    themeColors.push(defaults[themeColors.length]);
                }

                // Create background gradient using darker versions of the colors
                const darkenColor = (hex, amount = 0.7) => {
                    const r = parseInt(hex.slice(1, 3), 16);
                    const g = parseInt(hex.slice(3, 5), 16);
                    const b = parseInt(hex.slice(5, 7), 16);

                    const newR = Math.round(r * (1 - amount));
                    const newG = Math.round(g * (1 - amount));
                    const newB = Math.round(b * (1 - amount));

                    return `#${[newR, newG, newB].map(x => Math.max(0, x).toString(16).padStart(2, '0')).join('')}`;
                };

                const theme = {
                    name: 'dynamic',
                    primary: themeColors[0],
                    secondary: themeColors[1] || '#4CAF50',
                    accent: themeColors[2] || '#ff6b6b',
                    tertiary: themeColors[3] || '#9c27b0',
                    quaternary: themeColors[4] || '#ff9800',
                    background: `linear-gradient(135deg, ${darkenColor(themeColors[0], 0.8)} 0%, ${darkenColor(themeColors[1] || themeColors[0], 0.8)} 100%)`,
                    headerBorder: createGradient(sortedColors.slice(0, 4), '90deg'),
                    panelBorder: createGradient(sortedColors.slice().reverse().slice(0, 4), '90deg')
                };

                return [theme];
            }

            // Helper to return the default theme
            function getDefaultTheme() {
                return [{
                    name: 'default',
                    primary: '#0080ff',
                    secondary: '#4CAF50',
                    accent: '#ff6b6b',
                    tertiary: '#9c27b0',
                    quaternary: '#ff9800',
                    background: 'linear-gradient(135deg, #2a2a2a 0%, #2e2e2e 100%)',
                    headerBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)',
                    panelBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)'
                }];
            }

            function generateThemeFromColors(colors) {
                if (!colors || colors.length === 0) {
                    return [{
                        name: 'default',
                        primary: '#0080ff',
                        secondary: '#4CAF50',
                        accent: '#ff6b6b',
                        tertiary: '#9c27b0',
                        quaternary: '#ff9800',
                        background: 'linear-gradient(135deg, #2a2a2a 0%, #2e2e2e 100%)',
                        headerBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)',
                        panelBorder: 'linear-gradient(90deg, #0080ff 0%, #4CAF50 50%, #ff6b6b 100%)'
                    }];
                }

                // Convert RGB to hex
                const toHex = (r, g, b) => `#${[r, g, b].map(x => Math.round(x).toString(16).padStart(2, '0')).join('')}`;

                // Create gradient from multiple colors
                const createGradient = (colorArray, direction = '90deg') => {
                    if (colorArray.length === 1) {
                        return toHex(colorArray[0].r, colorArray[0].g, colorArray[0].b);
                    }

                    const colorStops = colorArray.slice(0, 6).map((color, index) => {
                        const percentage = (index / Math.max(1, colorArray.length - 1)) * 100;
                        return `${toHex(color.r, color.g, color.b)} ${percentage}%`;
                    }).join(', ');

                    return `linear-gradient(${direction}, ${colorStops})`;
                };

                // Sort colors by vibrancy (saturation * brightness)
                const sortedColors = colors.slice().sort((a, b) => {
                    const vibrancyA = Math.abs(Math.max(a.r, a.g, a.b) - Math.min(a.r, a.g, a.b)) * ((a.r + a.g + a.b) / 3);
                    const vibrancyB = Math.abs(Math.max(b.r, b.g, b.b) - Math.min(b.r, b.g, b.b)) * ((b.r + b.g + b.b) / 3);
                    return vibrancyB - vibrancyA;
                });

                // Select diverse colors for theme roles
                const themeColors = [];

                // Primary: Most vibrant color
                if (sortedColors[0]) themeColors.push(toHex(sortedColors[0].r, sortedColors[0].g, sortedColors[0].b));

                // Secondary: Find a contrasting color
                let secondaryIndex = 1;
                if (sortedColors.length > 2) {
                    // Look for a color that's different enough from primary
                    for (let i = 1; i < Math.min(sortedColors.length, 4); i++) {
                        const colorDiff = Math.abs(sortedColors[0].r - sortedColors[i].r) +
                            Math.abs(sortedColors[0].g - sortedColors[i].g) +
                            Math.abs(sortedColors[0].b - sortedColors[i].b);
                        if (colorDiff > 80) {
                            secondaryIndex = i;
                            break;
                        }
                    }
                }
                if (sortedColors[secondaryIndex]) themeColors.push(toHex(sortedColors[secondaryIndex].r, sortedColors[secondaryIndex].g, sortedColors[secondaryIndex].b));

                // Add remaining colors for accent, tertiary, quaternary
                for (let i = 0; i < sortedColors.length && themeColors.length < 5; i++) {
                    const hex = toHex(sortedColors[i].r, sortedColors[i].g, sortedColors[i].b);
                    if (!themeColors.includes(hex)) {
                        themeColors.push(hex);
                    }
                }

                // Fill with defaults if needed
                const defaults = ['#0080ff', '#4CAF50', '#ff6b6b', '#9c27b0', '#ff9800'];
                while (themeColors.length < 5) {
                    themeColors.push(defaults[themeColors.length]);
                }

                // Create background gradient using darker versions of the colors
                const darkenColor = (hex, amount = 0.3) => {
                    const r = parseInt(hex.slice(1, 3), 16);
                    const g = parseInt(hex.slice(3, 5), 16);
                    const b = parseInt(hex.slice(5, 7), 16);

                    const newR = Math.round(r * (1 - amount));
                    const newG = Math.round(g * (1 - amount));
                    const newB = Math.round(b * (1 - amount));

                    return `#${[newR, newG, newB].map(x => x.toString(16).padStart(2, '0')).join('')}`;
                };

                const theme = {
                    name: 'dynamic',
                    primary: themeColors[0],
                    secondary: themeColors[1] || '#4CAF50',
                    accent: themeColors[2] || '#ff6b6b',
                    tertiary: themeColors[3] || '#9c27b0',
                    quaternary: themeColors[4] || '#ff9800',
                    background: `linear-gradient(135deg, ${darkenColor(themeColors[0], 0.8)} 0%, ${darkenColor(themeColors[1] || themeColors[0], 0.8)} 100%)`,
                    headerBorder: createGradient(sortedColors.slice(0, 4), '90deg'),
                    panelBorder: createGradient(sortedColors.slice().reverse().slice(0, 4), '90deg')
                };

                return [theme];
            }

            function applyTheme(theme) {
                currentTheme = theme;

                // Update preview section background and border
                previewSection.style.background = theme.background;
                previewSection.style.border = `2px solid transparent`;
                previewSection.style.backgroundImage = `${theme.background}, ${theme.panelBorder}`;
                previewSection.style.backgroundOrigin = 'border-box';
                previewSection.style.backgroundClip = 'padding-box, border-box';

                // Calculate brightness of primary color to determine theme approach
                const primaryRgb = hexToRgb(theme.primary);
                const brightness = (primaryRgb.r * 299 + primaryRgb.g * 587 + primaryRgb.b * 114) / 1000;
                const isVeryBright = brightness > 180;

                // Enhanced header with dual approach: vibrant vs readable
                const secondaryRgb = hexToRgb(theme.secondary);
                const accentRgb = hexToRgb(theme.accent);

                if (isVeryBright) {
                    // For bright colors: Use vibrant background with strong text contrast
                    const vibrantGradient = `linear-gradient(135deg,
                        rgba(${primaryRgb.r}, ${primaryRgb.g}, ${primaryRgb.b}, 0.85) 0%,
                        rgba(${secondaryRgb.r}, ${secondaryRgb.g}, ${secondaryRgb.b}, 0.75) 50%,
                        rgba(${accentRgb.r}, ${accentRgb.g}, ${accentRgb.b}, 0.85) 100%)`;

                    characterHeader.style.background = vibrantGradient;
                    characterHeader.style.border = `3px solid ${theme.primary}`;
                    characterHeader.style.boxShadow = `
                        0 6px 25px rgba(${primaryRgb.r}, ${primaryRgb.g}, ${primaryRgb.b}, 0.4),
                        inset 0 1px 0 rgba(255,255,255,0.3),
                        inset 0 -1px 0 rgba(0,0,0,0.3)`;

                    // Strong text contrast for bright backgrounds
                    applyTextStyles('#000000', '0 1px 2px rgba(255,255,255,0.8)', '#1a5d1a', '#1a5d1a');

                } else {
                    // For darker colors: Use subtle overlay with good contrast
                    const subtleGradient = `linear-gradient(135deg,
                        rgba(${primaryRgb.r}, ${primaryRgb.g}, ${primaryRgb.b}, 0.25) 0%,
                        rgba(${secondaryRgb.r}, ${secondaryRgb.g}, ${secondaryRgb.b}, 0.20) 50%,
                        rgba(${accentRgb.r}, ${accentRgb.g}, ${accentRgb.b}, 0.25) 100%)`;

                    const patternOverlay = `radial-gradient(circle at 20% 80%, rgba(255,255,255,0.08) 0%, transparent 50%),
                                           radial-gradient(circle at 80% 20%, rgba(255,255,255,0.05) 0%, transparent 50%)`;

                    characterHeader.style.background = `${subtleGradient}, ${patternOverlay}, rgba(0,0,0,0.7)`;
                    characterHeader.style.border = `2px solid transparent`;
                    characterHeader.style.backgroundImage = `${subtleGradient}, ${patternOverlay}, rgba(0,0,0,0.7), ${theme.headerBorder}`;
                    characterHeader.style.backgroundOrigin = 'border-box';
                    characterHeader.style.backgroundClip = 'padding-box, border-box';
                    characterHeader.style.boxShadow = `
                        0 4px 20px rgba(${primaryRgb.r}, ${primaryRgb.g}, ${primaryRgb.b}, 0.3),
                        inset 0 1px 0 rgba(255,255,255,0.1),
                        inset 0 -1px 0 rgba(0,0,0,0.2)`;

                    // Light text for darker backgrounds
                    applyTextStyles('#ffffff', '0 2px 4px rgba(0,0,0,0.8)', '#4CAF50', '#4CAF50');
                }

                // Update title border and background
                previewTitle.style.borderBottomColor = theme.primary;
                previewTitle.style.background = `linear-gradient(90deg, ${theme.primary}20, ${theme.secondary}20, ${theme.accent}20)`;
                previewTitle.style.borderRadius = '4px';
                previewTitle.style.padding = '10px';

                // Update section icons to use primary color
                const sectionIcons = document.querySelectorAll('.collapsible-section svg');
                sectionIcons.forEach(icon => {
                    icon.style.color = theme.primary;
                });

                // Update all copy button colors to match new theme
                const copyButtons = document.querySelectorAll('.copy-btn');
                copyButtons.forEach(button => {
                    button.style.borderColor = `${theme.primary}40`;
                    button.style.color = theme.primary;
                    button.style.boxShadow = `0 2px 8px ${theme.primary}20`;
                    // Update SVG stroke color
                    const svg = button.querySelector('svg');
                    if (svg) svg.setAttribute('stroke', theme.primary);
                });
            }

            function applyTextStyles(nameColor, textShadow, tokenColor, chatNameColor) {
                const nameElement = document.getElementById('preview-character-name');
                const creatorElement = document.getElementById('preview-creator-info');
                const tokensElement = document.getElementById('preview-tokens');
                const chatNameElement = document.getElementById('preview-chat-name');

                if (nameElement) {
                    nameElement.style.color = nameColor;
                    nameElement.style.textShadow = textShadow;
                    nameElement.style.fontWeight = 'bold';
                }

                if (creatorElement) {
                    creatorElement.style.color = nameColor === '#000000' ? '#333333' : '#e0e0e0';
                    creatorElement.style.textShadow = textShadow;
                }

                if (tokensElement) {
                    tokensElement.style.color = tokenColor;
                    tokensElement.style.textShadow = textShadow;
                    tokensElement.style.fontWeight = 'bold';
                }

                if (chatNameElement) {
                    chatNameElement.style.color = chatNameColor;
                    chatNameElement.style.textShadow = textShadow;
                }
            }

            // Helper function to convert hex to RGB
            function hexToRgb(hex) {
                const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
                return result ? {
                    r: parseInt(result[1], 16),
                    g: parseInt(result[2], 16),
                    b: parseInt(result[3], 16)
                } : {
                    r: 0,
                    g: 128,
                    b: 255
                }; // fallback to blue
            }

            // Function to create collapsible section with enhanced bars and copy functionality (NO BOTTOM BARS)
            function createCollapsibleSection(id, title, iconSvg) {
                const sectionWrapper = makeElement(
                    "div", {
                        id: `section-${id}`,
                        className: "collapsible-section",
                    }, {
                        marginBottom: "12px",
                    },
                );

                // --- FIX: State management for copy button to prevent hover/click bugs ---
                let revertIconTimeout = null;
                let isSuccessState = false;
                let copyButtonAnimationTimeout = null;

                // Title above the panel (centered)
                const sectionTitle = makeElement(
                    "div", {}, {
                        textAlign: "center",
                        marginBottom: "8px",
                    },
                );

                const titleContent = makeElement(
                    "div", {}, {
                        display: "inline-flex",
                        alignItems: "center",
                        gap: "8px",
                        color: "#fff",
                        fontSize: "13px",
                        fontWeight: "600",
                        textTransform: "uppercase",
                        letterSpacing: "1px",
                    },
                );

                const titleIcon = makeElement(
                    "div", {
                        innerHTML: iconSvg,
                    }, {
                        display: "flex",
                        alignItems: "center",
                        color: currentTheme.primary,
                    },
                );

                const titleText = makeElement(
                    "span", {
                        textContent: title,
                    }, {},
                );

                titleContent.appendChild(titleIcon);
                titleContent.appendChild(titleText);
                sectionTitle.appendChild(titleContent);
                sectionWrapper.appendChild(sectionTitle);

                // Panel container
                const panelContainer = makeElement(
                    "div", {
                        className: "panel-container",
                    }, {
                        background: "#1a1a1a",
                        borderRadius: "6px",
                        border: "1px solid #444",
                        overflow: "hidden",
                        transition: "all 200ms ease",
                        position: "relative",
                    },
                );

                // Top bar (clickable) - enhanced with better gradient
                const topBar = makeElement(
                    "div", {
                        className: "panel-bar top-bar",
                    }, {
                        height: "12px",
                        background: "linear-gradient(90deg, #444 0%, #555 50%, #444 100%)",
                        cursor: "pointer",
                        transition: "all 200ms ease",
                        position: "relative",
                    },
                );

                // Content area
                const contentArea = makeElement(
                    "div", {
                        className: "content-area",
                    }, {
                        maxHeight: "0px",
                        overflow: "hidden",
                        transition: "max-height 350ms cubic-bezier(0.4, 0, 0.2, 1)",
                        background: "#222",
                        position: "relative", // Important for copy button positioning
                    },
                );

                const contentText = makeElement(
                    "div", {
                        className: "content-text",
                        id: `content-${id}`,
                    }, {
                        padding: "16px 50px 16px 20px", // Extra right padding for copy button
                        fontSize: "13px",
                        lineHeight: "1.6",
                        color: "#ddd",
                        whiteSpace: "pre-wrap",
                        wordBreak: "break-word",
                        position: "relative",
                        userSelect: "text", // Make text selectable
                        cursor: "text", // Show text cursor
                    },
                );

                // Copy button - positioned as proper overlay with theme colors
                const copyButton = makeElement(
                    "button", {
                        className: "copy-btn",
                        innerHTML: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2 2v1"/></svg>',
                        title: "Copy to clipboard",
                    }, {
                        position: "absolute",
                        top: "8px", // Moved down from top bar
                        right: "8px",
                        background: "transparent",
                        border: `1px solid ${currentTheme.primary}40`, // Use theme with opacity
                        borderRadius: "6px",
                        color: currentTheme.primary, // Use theme color
                        padding: "6px 8px",
                        cursor: "pointer",
                        fontSize: "12px",
                        opacity: "0.6", // More subtle idle state
                        transform: "scale(1)",
                        transition: "all 200ms ease",
                        zIndex: "1000", // High z-index for overlay
                        display: "none",
                        alignItems: "center",
                        justifyContent: "center",
                        minWidth: "28px",
                        minHeight: "28px",
                        boxShadow: `0 2px 8px ${currentTheme.primary}20`, // Theme-based shadow
                    },
                );

                // --- FIXED: Hover listeners now check the success state ---
                copyButton.addEventListener('mouseenter', () => {
                    if (isSuccessState) return; // Do nothing if in success state
                    copyButton.style.opacity = "1";
                    copyButton.style.transform = "scale(1.05)";
                    copyButton.style.borderColor = currentTheme.secondary;
                    copyButton.style.boxShadow = `0 4px 12px ${currentTheme.primary}40, 0 0 0 2px ${currentTheme.accent}30`;
                    copyButton.style.color = currentTheme.secondary;
                    const svg = copyButton.querySelector('svg');
                    if (svg) svg.setAttribute('stroke', currentTheme.secondary);
                });

                copyButton.addEventListener('mouseleave', () => {
                    if (isSuccessState) return; // Do nothing if in success state
                    copyButton.style.opacity = "0.6";
                    copyButton.style.transform = "scale(1)";
                    copyButton.style.borderColor = `${currentTheme.primary}40`;
                    copyButton.style.boxShadow = `0 2px 8px ${currentTheme.primary}20`;
                    copyButton.style.color = currentTheme.primary;
                    const svg = copyButton.querySelector('svg');
                    if (svg) svg.setAttribute('stroke', currentTheme.primary);
                });

                copyButton.addEventListener('click', (e) => {
                    e.stopPropagation();

                    clearTimeout(revertIconTimeout);
                    isSuccessState = false;

                    const content = contentText.textContent;
                    const placeholders = ["No description available", "No scenario available", "No first message available", "No examples available", "Definition not exposed"];
                    const isPlaceholder = placeholders.some(placeholder => content && content.trim() === placeholder);

                    if (content && content.trim() !== "" && !isPlaceholder) {
                        navigator.clipboard.writeText(content).then(() => {
                            showCopyNotification();
                            isSuccessState = true; // Enter success state

                            copyButton.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><polyline points="20,6 9,17 4,12"/></svg>';
                            copyButton.style.background = currentTheme.secondary || "#4CAF50";
                            copyButton.style.borderColor = currentTheme.secondary || "#66BB6A";
                            copyButton.style.color = "white";

                            revertIconTimeout = setTimeout(() => {
                                isSuccessState = false; // Exit success state
                                copyButton.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2 2v1"/></svg>';
                                copyButton.style.background = "transparent";
                                copyButton.style.borderColor = `${currentTheme.primary}40`;
                                copyButton.style.color = currentTheme.primary;
                                copyButton.style.boxShadow = `0 2px 8px ${currentTheme.primary}20`;
                                const svg = copyButton.querySelector('svg');
                                if (svg) svg.setAttribute('stroke', currentTheme.primary);
                            }, 1500);
                        }).catch((err) => {
                            debugError("Clipboard write failed:", err);
                        });
                    }
                });


                panelContainer.appendChild(topBar);
                panelContainer.appendChild(contentArea);
                panelContainer.appendChild(copyButton);

                contentArea.appendChild(contentText);
                contentArea.appendChild(copyButton);

                let isExpanded = sessionStorage.getItem(`section-${id}-expanded`) === "true";

                // This is the animated toggle for user clicks
                function toggleSection() {
                    clearTimeout(copyButtonAnimationTimeout);
                    isExpanded = !isExpanded;
                    sessionStorage.setItem(`section-${id}-expanded`, isExpanded);

                    if (isExpanded) {
                        const scrollHeight = contentText.scrollHeight + 20;
                        contentArea.style.maxHeight = scrollHeight + "px";
                        panelContainer.style.borderColor = currentTheme.primary;
                        topBar.style.background = `linear-gradient(90deg, ${currentTheme.primary} 0%, ${currentTheme.secondary} 50%, ${currentTheme.accent} 100%)`;
                        const hasValidContent = contentText.textContent && !isPlaceholderText(contentText.textContent.trim());
                        if (hasValidContent) {
                            copyButton.style.display = "flex";
                            requestAnimationFrame(() => {
                                copyButton.style.opacity = "0.6";
                                copyButton.style.transform = "scale(1)";
                            });
                        }
                    } else {
                        contentArea.style.maxHeight = "0px";
                        panelContainer.style.borderColor = "#444";
                        topBar.style.background = "linear-gradient(90deg, #444 0%, #555 50%, #444 100%)";

                        // Fade out copy button before hiding it
                        copyButton.style.opacity = "0";
                        copyButton.style.transform = "scale(0.8)";
                        copyButtonAnimationTimeout = setTimeout(() => {
                            copyButton.style.display = "none";
                        }, 200); // Match the fade transition duration
                    }

                }

                // --- FIXED: Set initial state ONCE on load, without animation ---
                // We wait for the DOM to settle to get the correct scrollHeight
                setTimeout(() => {
                    if (isExpanded) {
                        contentArea.style.transition = 'none'; // Temporarily disable animation

                        const scrollHeight = contentText.scrollHeight + 20;
                        contentArea.style.maxHeight = scrollHeight + "px";
                        panelContainer.style.borderColor = currentTheme.primary;
                        topBar.style.background = `linear-gradient(90deg, ${currentTheme.primary} 0%, ${currentTheme.secondary} 50%, ${currentTheme.accent} 100%)`;

                        const hasValidContent = contentText.textContent && !isPlaceholderText(contentText.textContent.trim());
                        if (hasValidContent) {
                            copyButton.style.display = "flex";
                            copyButton.style.opacity = "0.6";
                        }

                        // Re-enable animation for subsequent user clicks
                        requestAnimationFrame(() => {
                            contentArea.style.transition = 'max-height 350ms cubic-bezier(0.4, 0, 0.2, 1)';

                            // Restore scroll position after collapsibles are ready
                            if (typeof restoreScrollPosition === 'function') {
                                restoreScrollPosition();
                            }
                        });
                    } else {
                        // Also restore scroll for non-expanded sections
                        requestAnimationFrame(() => {
                            if (typeof restoreScrollPosition === 'function') {
                                restoreScrollPosition();
                            }
                        });
                    }
                }, 100);

                topBar.addEventListener('mouseenter', () => {
                    if (!isExpanded) {
                        topBar.style.background = "linear-gradient(90deg, #555 0%, #666 50%, #555 100%)";
                    }
                });
                topBar.addEventListener('mouseleave', () => {
                    if (!isExpanded) {
                        topBar.style.background = "linear-gradient(90deg, #444 0%, #555 50%, #444 100%)";
                    }
                });

                topBar.addEventListener('click', toggleSection);

                sectionWrapper.appendChild(panelContainer);

                return {
                    element: sectionWrapper,
                    setContent: (text) => {
                        const hasContent = text && text.trim() !== "" && !isPlaceholderText(text.trim());

                        if (hasContent) {
                            contentText.textContent = text;
                            contentText.style.color = "#ddd";
                            contentText.style.fontStyle = "normal";
                            contentText.style.textAlign = "center";
                            contentText.style.background = "transparent";
                            contentText.style.border = "none";
                            contentText.style.opacity = "1";
                            contentText.style.userSelect = "text";
                            contentText.style.cursor = "text";
                            contentText.style.padding = "16px 50px 16px 20px";
                        } else {
                            const displayText = text && text.trim() !== "" ? text : "No content available";
                            contentText.textContent = displayText;

                            let placeholderColor = currentTheme.accent || "#ff6b6b";
                            let placeholderBg = `${currentTheme.accent}15` || "#ff6b6b15";
                            let placeholderBorder = `${currentTheme.accent}40` || "#ff6b6b40";
                            if (displayText.includes("Definition not exposed")) {
                                placeholderColor = currentTheme.quaternary || "#ff9800";
                                placeholderBg = `${currentTheme.quaternary}15` || "#ff980015";
                                placeholderBorder = `${currentTheme.quaternary}40` || "#ff980040";
                            } else if (displayText.includes("No") && displayText.includes("available")) {
                                placeholderColor = currentTheme.tertiary || "#9e9e9e";
                                placeholderBg = `${currentTheme.tertiary}15` || "#9e9e9e15";
                                placeholderBorder = `${currentTheme.tertiary}40` || "#9e9e9e40";
                            }

                            contentText.style.color = placeholderColor;
                            contentText.style.fontStyle = "italic";
                            contentText.style.opacity = "0.8";
                            contentText.style.textAlign = "center";
                            contentText.style.background = `linear-gradient(135deg, ${placeholderBg} 0%, transparent 100%)`;
                            contentText.style.borderRadius = "4px";
                            contentText.style.border = `1px dashed ${placeholderBorder}`;
                            contentText.style.fontSize = "12px";
                            contentText.style.userSelect = "none";
                            contentText.style.cursor = "default";
                            contentText.style.padding = "16px 20px";
                        }

                        // This part is for when content is set AFTER initial render.
                        // It helps update the state correctly.
                        if (isExpanded) {
                            if (hasContent) {
                                copyButton.style.display = "flex";
                            } else {
                                copyButton.style.display = "none";
                            }
                            // A small delay to ensure `scrollHeight` is updated in the DOM
                            setTimeout(() => {
                                contentArea.style.maxHeight = (contentText.scrollHeight + 20) + "px";
                            }, 50);
                        }
                    }
                };
            }

            // Simplified notification system - no queue, just replace
            let notificationTimeout = null;

            function showCopyNotification(message = "Copied to clipboard!") {
                // Clear any existing timeout
                if (notificationTimeout) {
                    clearTimeout(notificationTimeout);
                }

                // Remove any existing notification immediately
                const existingNotification = document.getElementById('copy-notification');
                if (existingNotification) {
                    existingNotification.remove();
                }

                const notification = makeElement(
                    "div", {
                        id: "copy-notification",
                        textContent: message,
                    }, {
                        position: "fixed",
                        top: "20px",
                        right: "20px",
                        background: `linear-gradient(135deg, ${currentTheme.primary}dd, ${currentTheme.secondary}dd)`,
                        color: "white",
                        padding: "12px 20px",
                        borderRadius: "6px",
                        fontSize: "14px",
                        fontWeight: "500",
                        zIndex: "99999",
                        boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
                        transform: "translateX(100%)",
                        transition: "transform 300ms ease",
                        border: `2px solid ${currentTheme.accent}`,
                        maxWidth: "300px",
                        wordBreak: "break-word",
                    },
                );

                document.body.appendChild(notification);

                // Animate in
                setTimeout(() => {
                    notification.style.transform = "translateX(0)";
                }, 10);

                // Animate out and remove
                notificationTimeout = setTimeout(() => {
                    notification.style.transform = "translateX(100%)";
                    setTimeout(() => {
                        if (notification.parentNode) {
                            notification.parentNode.removeChild(notification);
                        }
                    }, 300);
                }, 2500);
            }

            // Create sections with custom SVG icons (NO FontAwesome)
            const descriptionSection = createCollapsibleSection('description', 'Description', customIcons.description);
            const scenarioSection = createCollapsibleSection('scenario', 'Scenario', customIcons.scenario);
            const firstMessageSection = createCollapsibleSection('firstMessage', 'First Message', customIcons.firstMessage);
            const examplesSection = createCollapsibleSection('examples', 'Examples', customIcons.examples);

            sectionsContainer.appendChild(descriptionSection.element);
            sectionsContainer.appendChild(scenarioSection.element);
            sectionsContainer.appendChild(firstMessageSection.element);
            sectionsContainer.appendChild(examplesSection.element);

            previewSection.appendChild(sectionsContainer);


            // Cache for character preview data
            let previewDataCache = {
                characterId: null,
                data: null,
                avatarUrl: null,
                themeApplied: false
            };

            // Function to calculate approximate token count
            function calculateTokenCount(text) {
                if (!text) return 0;
                // Rough estimation: 1 token ≈ 4 characters for English text
                return Math.round(text.length / 4);
            }

            // Function to modify avatar URL to use width=200
            function modifyAvatarUrl(originalUrl) {
                if (!originalUrl) return null;
                return originalUrl.replace(/width=\d+/, 'width=200');
            }

            // Extract token info from DOM when definition is not exposed
            // Extract token info from DOM when definition is not exposed
            function extractTokenInfoFromDOM() {
                try {
                    // Look for the specific character info header with token information
                    const tokenHeaders = document.querySelectorAll('h4._characterInfoHeader_1tqgq_38.glow-on-hover, h4[class*="characterInfoHeader"]');

                    for (const header of tokenHeaders) {
                        const headerText = header.textContent || '';
                        if (headerText.includes('Character Definition is hidden') && headerText.includes('Total') && headerText.includes('tokens')) {
                            // Extract: "Character Definition is hidden, Total 1889 tokens, Permanent 1075"
                            const tokenMatch = headerText.match(/Total (\d+(?:,\d+)*) tokens/i);
                            if (tokenMatch) {
                                const tokenCount = tokenMatch[1].replace(/,/g, '');
                                return `Total ${parseInt(tokenCount).toLocaleString()} tokens`;
                            }
                        }
                    }

                    return "Token info not available";
                } catch (error) {
                    debugLog("Error extracting token info from DOM:", error);
                    return "Token info not available";
                }
            }

            // Enhanced stats extraction function - FINALIZED AND STREAMLINED
            async function getEnhancedCharacterStats(characterId, characterCardUrl) {
                // Initialize stats object with only the fields we can reliably fetch.
                const stats = {
                    totalChats: "0",
                    totalMessages: "0",
                    tokenInfo: null
                };

                try {
                    // Use the simple, reliable fetch method for static content.
                    const response = await fetch(characterCardUrl);
                    const html = await response.text();
                    const doc = new DOMParser().parseFromString(html, "text/html");

                    // Extract token info for hidden definitions
                    try {
                        const tokenHeaders = doc.querySelectorAll('h4[class*="characterInfoHeader"]');
                        for (const header of tokenHeaders) {
                            const headerText = header.textContent || '';
                            if (headerText.includes('Character Definition is hidden') && headerText.includes('Total') && headerText.includes('tokens')) {
                                const tokenMatch = headerText.match(/Total (\d+(?:,\d+)*) tokens/i);
                                if (tokenMatch) {
                                    const tokenCount = tokenMatch[1].replace(/,/g, '');
                                    stats.tokenInfo = `Total ${parseInt(tokenCount).toLocaleString()} tokens`;
                                }
                                break;
                            }
                        }
                    } catch (error) {
                        debugLog("Error extracting token info:", error);
                    }

                    // Extract total chats and messages
                    try {
                        // This selector has proven to be reliable for the static content.
                        const statsContainer = doc.querySelector('div.chakra-stack.css-1qoq49l');
                        if (statsContainer) {
                            const statElements = statsContainer.querySelectorAll('p.chakra-text.css-0');
                            if (statElements.length >= 2) {
                                stats.totalChats = statElements[0].textContent.trim();
                                stats.totalMessages = statElements[1].textContent.trim();
                                debugLog(`Successfully fetched Total Chats: ${stats.totalChats}, Total Messages: ${stats.totalMessages}`);
                            }
                        } else {
                            debugWarn("Could not find the container for Total Chats/Messages.");
                        }
                    } catch (error) {
                        debugLog("Error extracting chat/message stats:", error);
                    }

                } catch (error) {
                    debugError("An error occurred while fetching character page stats:", error);
                }

                return stats;
            }

            // Function to update character preview with proper data retrieval and caching
            // Function to update character preview with proper data retrieval and caching
            async function updateCharacterPreview() {
                if (!chatData || !chatData.character) {
                    debugLog("Preview update stopped: No character data available.");
                    return;
                }

                const characterId = chatData.character.id;
                const cacheKey = `char_preview_cache_${characterId}`;

                // --- MODIFIED: Check sessionStorage for cached data first ---
                try {
                    const cachedDataJSON = sessionStorage.getItem(cacheKey);
                    if (cachedDataJSON) {
                        debugLog("Using cached character preview data from sessionStorage for ID:", characterId);
                        const cachedData = JSON.parse(cachedDataJSON);
                        updatePreviewUI(cachedData); // Update UI from cache
                        return; // Stop execution, we don't need to fetch again
                    }
                } catch (e) {
                    debugWarn("Could not parse cached preview data, fetching fresh.", e);
                    sessionStorage.removeItem(cacheKey); // Clear corrupted data
                }

                debugLog("No cached data found. Fetching fresh character data for ID:", characterId);

                try {
                    const meta = await getCharacterMeta();
                    const tokens = await getFilenameTokens(meta);

                    const charAvatar = document.querySelector('img[src*="/bot-avatars/"]');
                    const avatarUrl = charAvatar ? modifyAvatarUrl(charAvatar.src) : null;

                    const enhancedStats = await getEnhancedCharacterStats(characterId, meta.characterCardUrl);

                    // Compile all data into one object
                    const characterData = {
                        id: characterId,
                        name: meta.name || chatData.character.name || "Unknown",
                        chatName: chatData.character.chat_name,
                        creator: tokens.creator || "Unknown",
                        definitionExposed: meta.definitionExposed,
                        avatarUrl: avatarUrl,
                        meta: meta,
                        description: meta.definitionExposed ? (meta.personality || chatData.character.description || "No description available") : "Definition not exposed",
                        scenario: meta.definitionExposed ? (meta.scenario || "No scenario available") : "Definition not exposed",
                        firstMessage: meta.firstMessage || chatData.chatMessages?.[0]?.message || "No first message available",
                        examples: meta.definitionExposed ? (meta.exampleDialogs || "No examples available") : "Definition not exposed",
                        tokenCount: meta.definitionExposed ? calculateTokenCount([meta.personality, meta.scenario, meta.firstMessage, meta.exampleDialogs].filter(Boolean).join(" ")) : 0,
                        ...enhancedStats // Add stats from the fetched data
                    };

                    // --- MODIFIED: Save the freshly fetched data to sessionStorage ---
                    try {
                        sessionStorage.setItem(cacheKey, JSON.stringify(characterData));
                        debugLog("Character data cached in sessionStorage for ID:", characterId);
                    } catch (e) {
                        debugWarn("Could not save character data to sessionStorage, it may be too large.", e);
                    }


                    // Update the UI with the new data
                    updatePreviewUI(characterData);

                } catch (err) {
                    debugLog("Failed to fetch and update character preview:", err);
                }
            }

            // Function to update the preview UI with character data
            // Function to update the preview UI with character data
            // Function to update the preview UI with character data
            function updatePreviewUI(characterData) {
                // Update basic info
                const displayName = useChatNameForName && characterData.chatName ?
                    characterData.chatName : characterData.name;

                // Make character name clickable to character page
                const characterUrl = characterData.meta?.characterCardUrl || '';
                if (characterUrl) {
                    characterName.innerHTML = `<a href="${characterUrl}" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: none; cursor: pointer; transition: opacity 0.2s ease;" onmouseover="this.style.opacity='0.7'" onmouseout="this.style.opacity='1'">${displayName}</a>`;
                } else {
                    characterName.textContent = displayName;
                }

                // Show chat name if different from display name
                if (characterData.chatName &&
                    characterData.chatName !== displayName &&
                    characterData.chatName !== characterData.name) {
                    characterChatName.textContent = `Chat Name: ${characterData.chatName}`;
                    characterChatName.style.display = 'block';
                } else {
                    characterChatName.style.display = 'none';
                }

                // Update creator info with clickable link
                const creatorUrl = characterData.meta?.creatorUrl || '';
                if (creatorUrl) {
                    characterCreator.innerHTML = `Creator: <a href="${creatorUrl}" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: none; cursor: pointer; transition: opacity 0.2s ease;" onmouseover="this.style.opacity='0.7'" onmouseout="this.style.opacity='1'">${characterData.creator}</a>`;
                } else {
                    characterCreator.textContent = `Creator: ${characterData.creator}`;
                }

                // Stats display - only shows what is reliably available
                const statsHTML = [];

                if (characterData.totalChats && characterData.totalChats !== "0") {
                    statsHTML.push(`💬 ${characterData.totalChats} chats`);
                }

                if (characterData.totalMessages && characterData.totalMessages !== "0") {
                    statsHTML.push(`📧 ${characterData.totalMessages} messages`);
                }

                // Update avatar with modified URL (width=200) and apply theme
                if (characterData.avatarUrl) {
                    avatarImg.src = characterData.avatarUrl;

                    // Apply dynamic theme based on avatar (only once per character)
                    if (!previewDataCache.themeApplied) {
                        avatarImg.onload = async () => {
                            try {
                                const themes = await analyzeImageColors(avatarImg);
                                const selectedTheme = themes[0];
                                applyTheme(selectedTheme);
                                previewDataCache.themeApplied = true;
                            } catch (error) {
                                debugLog("Theme analysis failed:", error);
                            }
                        };
                    }
                }

                // Update token count and status
                if (characterData.definitionExposed && characterData.tokenCount !== null) {
                    let tokenText = `~${characterData.tokenCount.toLocaleString()} tokens`;
                    if (statsHTML.length > 0) {
                        tokenText += ` • ${statsHTML.join(' • ')}`;
                    }
                    characterTokens.innerHTML = tokenText;
                    characterTokens.style.color = "#4CAF50";

                    // Update section content with actual data
                    descriptionSection.setContent(characterData.description);
                    scenarioSection.setContent(characterData.scenario);
                    firstMessageSection.setContent(characterData.firstMessage);
                    examplesSection.setContent(characterData.examples);
                } else {
                    // Show limited preview with token info if available
                    let limitedText = "⚠️ Limited preview - definition not exposed";
                    if (characterData.tokenInfo) {
                        limitedText = `⚠️ ${characterData.tokenInfo} - definition not exposed`;
                    }
                    if (statsHTML.length > 0) {
                        limitedText += ` • ${statsHTML.join(' • ')}`;
                    }
                    characterTokens.innerHTML = limitedText;
                    characterTokens.style.color = "#ff9800";

                    // Use limited data
                    descriptionSection.setContent(characterData.description);
                    scenarioSection.setContent(characterData.scenario);
                    firstMessageSection.setContent(characterData.firstMessage);
                    examplesSection.setContent(characterData.examples);
                }

                // Show preview section with animation
                previewSection.style.display = "block";
                requestAnimationFrame(() => {
                    previewSection.style.opacity = "1";
                    previewSection.style.transform = "translateY(0)";
                });
            }

            // Helper function to check if text is a placeholder
            function isPlaceholderText(text) {
                const placeholders = [
                    "No description available",
                    "No scenario available",
                    "No first message available",
                    "No examples available",
                    "Definition not exposed",
                    "No content available"
                ];
                return placeholders.includes(text);
            }


            // Add the preview section to the export content
            exportContent.appendChild(previewSection);

            // Add scroll memory for export tab
            exportContent.addEventListener("scroll", () => {
                sessionStorage.setItem("char_export_scroll", exportContent.scrollTop);
            });

            // // Function to restore scroll position after collapsibles are ready
            // function restoreScrollPosition() {
            //     const savedExportScroll = parseInt(
            //         sessionStorage.getItem("char_export_scroll") || "0",
            //         10,
            //     );
            //     if (savedExportScroll > 0) {
            //         exportContent.scrollTop = savedExportScroll;
            //     }
            // }


            // Initialize preview update - only call once when UI is created
            if (chatData && chatData.character && viewActive) {
                updateCharacterPreview();
            }

            // // Restore export tab scroll on tab switch
            // function switchTab(tabKey) {
            //     if (tabKey === "export") {
            //         // Restore export tab scroll position
            //         requestAnimationFrame(() => {
            //             const savedExportScroll = parseInt(
            //                 sessionStorage.getItem("char_export_scroll") || "0",
            //                 10,
            //             );
            //             exportContent.scrollTop = savedExportScroll;
            //         });
            //     }
            // }

            // Add CSS styles for the enhanced collapsible sections
            if (!document.getElementById("char-preview-styles")) {
                const previewStyle = document.createElement("style");
                previewStyle.id = "char-preview-styles";
                // --- MODIFIED: Removed the 'background' property from the .copy-btn:hover rule ---
                previewStyle.textContent = `
                    .collapsible-section .panel-container:hover {
                        border-color: #666;
                        box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                    }

                    .panel-bar {
                        position: relative;
                    }

                    .panel-bar:hover::before {
                        content: '';
                        position: absolute;
                        top: 0;
                        left: 0;
                        right: 0;
                        bottom: 0;
                        background: rgba(255,255,255,0.1);
                        pointer-events: none;
                    }

                    .content-area {
                        background: linear-gradient(135deg, #222 0%, #1a1a1a 100%);
                    }

                    .content-text:empty::before {
                        content: "No content available";
                        color: #666;
                        font-style: italic;
                    }

                    .copy-btn:hover {
                        /* The blue background color has been removed from this rule. */
                        transform: scale(1.1) !important;
                    }

                    .copy-btn:active {
                        transform: scale(0.95) !important;
                    }
                `;
                document.head.appendChild(previewStyle);
            }

            // Add content border and alignment fixes
            if (!document.getElementById("char-content-border-fix")) {
                const contentBorderStyle = document.createElement("style");
                contentBorderStyle.id = "char-content-border-fix";
                // --- FIXED: Removed the 'border-right: none' rules ---
                contentBorderStyle.textContent = `
                    .content-text {
                        text-align: center !important;
                        padding: 16px 50px 16px 20px;
                    }
                `;
                document.head.appendChild(contentBorderStyle);
            }

            const contentWrapper = makeElement(
                "div", {
                    id: "content-wrapper",
                }, {
                    minHeight: "320px",
                    width: "100%",
                    overflow: "visible",
                    display: "flex",
                    flexDirection: "column",
                    justifyContent: "flex-start",
                    position: "relative",
                    flex: "1",
                },
            );
            gui.appendChild(contentWrapper);

            const tabContentStyles = {
                height: "100%",
                width: "100%",
                overflowY: "auto",
                overflowX: "hidden",
                padding: "15px",
                paddingTop: "10px",
                position: "absolute",
                top: "0",
                left: "0",
                opacity: "1",
                transform: "scale(1)",
                transition: `opacity ${TAB_ANIMATION_DURATION}ms ease, transform ${TAB_ANIMATION_DURATION}ms ease`,
                boxSizing: "border-box",
            };

            Object.assign(exportContent.style, tabContentStyles);
            exportContent.style.overflowY = "visible";
            exportContent.style.overflowX = "hidden";
            exportContent.style.display = "flex";
            exportContent.style.flexDirection = "column";
            exportContent.style.alignItems = "center";

            const settingsContent = makeElement(
                "div", {
                    id: "settings-tab",
                    style: "display: none;",
                },
                tabContentStyles,
            );

            contentWrapper.appendChild(exportContent);
            contentWrapper.appendChild(settingsContent);
            const savedExportScroll = parseInt(
                sessionStorage.getItem("char_export_scroll") || "0",
                10,
            );
            exportContent.scrollTop = savedExportScroll;
            const savedSettingsScroll = parseInt(
                sessionStorage.getItem("char_settings_scroll") || "0",
                10,
            );
            settingsContent.scrollTop = savedSettingsScroll;
            settingsContent.addEventListener("scroll", () =>
                sessionStorage.setItem("char_settings_scroll", settingsContent.scrollTop),
            );

            requestAnimationFrame(() => {
                exportContent.scrollTop = savedExportScroll;
                settingsContent.scrollTop = savedSettingsScroll;
            });

            const settingsTitle = makeElement(
                "h1", {
                    textContent: "Export Settings",
                }, {
                    margin: "-10px 0 25px 0",
                    fontSize: "24px",
                    paddingTop: "0px",
                    textAlign: "center",
                    fontWeight: "bold",
                    color: "#fff",
                    borderBottom: "2px solid #444",
                    paddingBottom: "12px",
                },
            );
            settingsContent.appendChild(settingsTitle);

            /**
             * Custom overlay scrollbar implementation
             * Creates a fully custom scrollbar that overlays the content
             * @param {HTMLElement} element - Target scrollable element
             */
            const createStaticScrollbar = (element) => {
                let track, thumb;
                let isDragging = false;
                let startY = 0;
                let startScrollTop = 0;
                let isVisible = false;

                const createScrollbarElements = () => {
                    // Create track
                    track = makeElement("div", {
                        className: "custom-scrollbar-track"
                    }, {});

                    // Create thumb
                    thumb = makeElement("div", {
                        className: "custom-scrollbar-thumb"
                    }, {});

                    track.appendChild(thumb);

                    // Add to container
                    const container = element.parentElement;
                    if (container) {
                        container.classList.add("custom-scrollbar-container");
                        container.style.position = "relative";
                        container.appendChild(track);
                    }

                    // Initialize in hidden state
                    track.style.visibility = "hidden";
                    track.style.opacity = "0";
                };

                const updateThumbSize = () => {
                    if (!track || !thumb) return;

                    const containerHeight = element.clientHeight;
                    const contentHeight = element.scrollHeight;
                    const scrollRatio = containerHeight / contentHeight;

                    if (scrollRatio >= 1 || contentHeight <= containerHeight) {
                        track.style.visibility = "hidden";
                        track.style.opacity = "0";
                        isVisible = false;
                        return;
                    }

                    track.style.visibility = "visible";
                    track.style.opacity = "0.6";
                    isVisible = true;

                    const thumbHeight = Math.max(20, containerHeight * scrollRatio);
                    thumb.style.height = `${thumbHeight}px`;

                    updateThumbPosition();
                };

                const show = () => {
                    if (track) {
                        updateThumbSize();
                    }
                };

                const hide = () => {
                    if (track) {
                        track.style.visibility = "hidden";
                        track.style.opacity = "0";
                        isVisible = false;
                    }
                };

                const updateThumbPosition = () => {
                    if (!thumb || !isVisible) return;

                    const containerHeight = element.clientHeight;
                    const contentHeight = element.scrollHeight;
                    const trackHeight = track.clientHeight;
                    const thumbHeight = thumb.clientHeight;
                    const maxThumbTop = trackHeight - thumbHeight;
                    const scrollRatio =
                        element.scrollTop / (contentHeight - containerHeight);
                    const thumbTop = scrollRatio * maxThumbTop;

                    thumb.style.transition = "none";
                    thumb.style.transform = `translateY(${thumbTop}px)`;
                    requestAnimationFrame(() => {
                        thumb.style.transition = "background 50ms ease";
                    });
                };

                const onThumbMouseDown = (e) => {
                    e.preventDefault();
                    e.stopPropagation();

                    isDragging = true;
                    startY = e.clientY;
                    startScrollTop = element.scrollTop;

                    thumb.classList.add("dragging");
                    track.classList.add("expanded");
                    document.addEventListener("mousemove", onMouseMove);
                    document.addEventListener("mouseup", onMouseUp);
                    document.body.style.userSelect = "none";
                };

                const onMouseMove = (e) => {
                    if (!isDragging) return;

                    const deltaY = e.clientY - startY;
                    const trackHeight = track.clientHeight;
                    const thumbHeight = thumb.clientHeight;
                    const contentHeight = element.scrollHeight;
                    const containerHeight = element.clientHeight;

                    const scrollRange = contentHeight - containerHeight;
                    const thumbRange = trackHeight - thumbHeight;
                    const scrollRatio = deltaY / thumbRange;
                    const scrollDelta = scrollRatio * scrollRange;

                    element.scrollTop = Math.max(
                        0,
                        Math.min(startScrollTop + scrollDelta, scrollRange),
                    );
                };

                const onMouseUp = () => {
                    isDragging = false;
                    thumb.classList.remove("dragging");
                    track.classList.remove("expanded");
                    document.removeEventListener("mousemove", onMouseMove);
                    document.removeEventListener("mouseup", onMouseUp);
                    document.body.style.userSelect = "";
                };

                const onTrackClick = (e) => {
                    if (e.target === thumb) return;

                    const trackRect = track.getBoundingClientRect();
                    const clickY = e.clientY - trackRect.top;
                    const trackHeight = track.clientHeight;
                    const thumbHeight = thumb.clientHeight;
                    const containerHeight = element.clientHeight;
                    const contentHeight = element.scrollHeight;

                    const scrollRatio =
                        (clickY - thumbHeight / 2) / (trackHeight - thumbHeight);
                    const scrollTop = scrollRatio * (contentHeight - containerHeight);

                    element.scrollTop = Math.max(
                        0,
                        Math.min(scrollTop, contentHeight - containerHeight),
                    );
                };

                const onScroll = () => {
                    updateThumbPosition();
                };

                const onResize = () => {
                    updateThumbSize();
                };

                // Initialize immediately
                (() => {
                    createScrollbarElements();

                    const onWheel = () => {
                        updateThumbPosition();
                    };

                    // Event listeners
                    element.addEventListener("scroll", onScroll);
                    element.addEventListener("wheel", onWheel, {
                        passive: true
                    });
                    thumb.addEventListener("mousedown", onThumbMouseDown);
                    track.addEventListener("click", onTrackClick);

                    // Resize observer for content changes
                    const resizeObserver = new ResizeObserver(onResize);
                    resizeObserver.observe(element);

                    // Start hidden
                    hide();
                })();

                return {
                    show,
                    hide,
                    updateThumbSize,
                    destroy: () => {
                        element.removeEventListener("scroll", onScroll);
                        element.removeEventListener("wheel", onWheel);
                        if (thumb) thumb.removeEventListener("mousedown", onThumbMouseDown);
                        if (track) {
                            track.removeEventListener("click", onTrackClick);
                            track.remove();
                        }
                        resizeObserver.disconnect();
                    }
                };
            };

            // Initialize custom scrollbar only when needed
            settingsScrollbar = createStaticScrollbar(settingsContent);

            // Filename Template Section
            const templateSection = makeElement(
                "div", {}, {
                    marginTop: "20px",
                    marginBottom: "15px",
                    padding: "15px",
                    background: "#2a2a2a",
                    borderRadius: "10px",
                    boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                    transition: "all 200ms ease",
                    cursor: "default",
                },
            );

            const templateTitle = makeElement(
                "h3", {
                    textContent: "Filename",
                }, {
                    margin: "0 0 15px 0",
                    fontSize: "16px",
                    color: "#fff",
                    fontWeight: "bold",
                    textAlign: "center",
                },
            );
            templateSection.appendChild(templateTitle);

            const templateInputContainer = makeElement(
                "div", {
                    className: "template-input-container",
                }, {
                    marginBottom: "12px",
                    position: "relative",
                    overflow: "hidden",
                },
            );

            const templateInput = makeElement(
                "input", {
                    type: "text",
                    value: filenameTemplate,
                    placeholder: "Enter filename template",
                }, {
                    width: "100%",
                    padding: "10px 55px 10px 12px",
                    border: "1px solid #555",
                    borderRadius: "6px",
                    background: "#1a1a1a",
                    color: "#fff",
                    fontSize: "13px",
                    boxSizing: "border-box",
                    transition: "border-color 200ms ease, box-shadow 200ms ease",
                    outline: "none",
                },
            );

            // Disable tooltips completely
            templateInput.setAttribute("title", "");
            templateInput.setAttribute("data-tooltip-disabled", "true");
            templateInput.removeAttribute("title");
            templateInput.addEventListener("mouseenter", (e) => {
                e.target.removeAttribute("title");
                e.target.title = "";
            });

            // Override browser tooltip styles
            templateInput.style.setProperty("pointer-events", "auto", "important");
            templateInput.addEventListener("mouseover", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });

            let hasChanges =
                filenameTemplate !== "" &&
                filenameTemplate !==
                (localStorage.getItem("filenameTemplate") || "{name}");
            let originalTemplate = localStorage.getItem("filenameTemplate") || "{name}";

            // Auto-restore functionality
            const restoreTemplate = () => {
                const savedDraft = localStorage.getItem("filenameTemplateDraft");
                if (savedDraft && savedDraft !== templateInput.value) {
                    templateInput.value = savedDraft;
                    filenameTemplate = savedDraft;
                    hasChanges =
                        filenameTemplate !== "" &&
                        filenameTemplate !==
                        (localStorage.getItem("filenameTemplate") || "{name}");

                    // Update apply button state
                    const applyBtn = templateSection.querySelector(".template-apply-btn");
                    if (applyBtn && hasChanges) {
                        applyBtn.style.opacity = "1";
                        applyBtn.style.color = "#4CAF50";
                        applyBtn.style.filter = "drop-shadow(0 0 4px rgba(76, 175, 80, 0.6))";
                    }
                }
            };

            templateInput.addEventListener("input", (e) => {
                filenameTemplate = e.target.value;
                localStorage.setItem("filenameTemplateDraft", filenameTemplate);
                hasChanges =
                    filenameTemplate !== "" &&
                    filenameTemplate !==
                    (localStorage.getItem("filenameTemplate") || "{name}");

                // Update apply button state with smooth animation
                const applyBtn = templateSection.querySelector(".template-apply-btn");
                if (applyBtn && !isAnimating) {
                    applyBtn.style.transition = "all 200ms ease";
                    if (hasChanges) {
                        applyBtn.style.opacity = "1";
                        applyBtn.style.color = "#4CAF50";
                        applyBtn.style.filter = "drop-shadow(0 0 4px rgba(76, 175, 80, 0.6))";
                    } else {
                        applyBtn.style.opacity = "0.5";
                        applyBtn.style.color = "#666";
                        applyBtn.style.filter = "none";
                    }
                }
            });

            // Restore on tab switch and UI open
            restoreTemplate();

            let isEnterHeld = false;
            let isMouseHeld = false;

            templateInput.addEventListener("keydown", (e) => {
                if (e.key === "Enter" && !isEnterHeld) {
                    isEnterHeld = true;
                    e.preventDefault();
                    if (hasChanges && !isAnimating) {
                        performApplyAction();
                    } else if (!hasChanges && !isAnimating) {
                        startGrayHold();
                    }
                }
            });

            templateInput.addEventListener("keyup", (e) => {
                if (e.key === "Enter") {
                    isEnterHeld = false;
                    if (!hasChanges && !isAnimating) {
                        endGrayHold();
                    }
                }
            });

            templateInput.addEventListener("mouseover", () => {
                if (
                    !isAnimating &&
                    document.activeElement !== templateInput &&
                    templateInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    templateInput.style.borderColor = "#777";
                }
            });

            templateInput.addEventListener("mouseout", () => {
                if (
                    !isAnimating &&
                    document.activeElement !== templateInput &&
                    templateInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    templateInput.style.borderColor = "#555";
                }
            });

            templateInput.addEventListener("focus", () => {
                if (
                    !isAnimating &&
                    templateInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    templateInput.style.borderColor = "#888";
                }
            });

            templateInput.addEventListener("blur", () => {
                if (
                    !isAnimating &&
                    templateInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    templateInput.style.borderColor = "#555";
                }
            });

            templateInputContainer.appendChild(templateInput);

            const applyButton = makeElement(
                "button", {
                    textContent: "✓",
                    className: "template-apply-btn",
                }, {
                    position: "absolute",
                    right: "8px",
                    top: "50%",
                    transform: "translateY(-50%)",
                    background: "transparent",
                    border: "none",
                    color: "#666",
                    fontSize: "14px",
                    cursor: "pointer",
                    padding: "4px",
                    borderRadius: "3px",
                    opacity: "0.5",
                    transition: "all 200ms ease",
                },
            );

            let isAnimating = false;

            const performApplyAction = () => {
                if (!hasChanges || isAnimating) return;
                isAnimating = true;

                // Green glow for input box (slightly thicker but subtle)
                templateInput.classList.add("applying-changes");
                templateInput.style.borderColor = "#4CAF50";
                templateInput.style.boxShadow = "0 0 8px rgba(76, 175, 80, 0.5)";

                // Show bright green glow before animation
                applyButton.style.color = "#2ecc71";
                applyButton.style.filter = "drop-shadow(0 0 8px rgba(46, 204, 113, 0.8))";
                applyButton.style.textShadow = "0 0 12px rgba(46, 204, 113, 0.9)";

                setTimeout(() => {
                    // Save current template
                    localStorage.setItem("filenameTemplate", filenameTemplate);
                    originalTemplate = filenameTemplate;
                    hasChanges = false;

                    // Slot machine animation sequence with proper overflow clipping

                    // Phase 1: Checkmark slides up with fast acceleration (like slot reel)
                    applyButton.style.transition =
                        "all 180ms cubic-bezier(0.25, 0.46, 0.45, 0.94)";
                    applyButton.style.transform = "translateY(-50%) translateY(-30px)";
                    applyButton.style.filter = "blur(4px)";
                    applyButton.style.opacity = "0";

                    setTimeout(() => {
                        // Phase 2: Applied! slides up from bottom (like slot winning symbol)
                        const appliedText = makeElement(
                            "span", {
                                textContent: "Applied!",
                            }, {
                                position: "absolute",
                                right: "8px",
                                top: "50%",
                                transform: "translateY(-50%) translateY(25px)",
                                color: "#4CAF50",
                                fontSize: "11px",
                                fontWeight: "bold",
                                opacity: "0",
                                transition: "all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94)",
                                pointerEvents: "none",
                                whiteSpace: "nowrap",
                            },
                        );

                        templateInputContainer.appendChild(appliedText);

                        // Applied! slides into view with deceleration
                        requestAnimationFrame(() => {
                            appliedText.style.opacity = "1";
                            appliedText.style.transform = "translateY(-50%) translateY(0px)";
                            appliedText.style.filter = "blur(0px)";
                        });

                        // Phase 3: Applied! slides up and disappears (faster stay time)
                        setTimeout(() => {
                            appliedText.style.transition =
                                "all 120ms cubic-bezier(0.25, 0.46, 0.45, 0.94)";
                            appliedText.style.transform = "translateY(-50%) translateY(-25px)";
                            appliedText.style.filter = "blur(3px)";
                            appliedText.style.opacity = "0";

                            // Phase 4: New checkmark slides up from bottom with glow
                            setTimeout(() => {
                                // Position new checkmark below input box ready to slide up
                                applyButton.style.transition = "none";
                                applyButton.style.transform = "translateY(-50%) translateY(25px)";
                                applyButton.style.opacity = "0";
                                applyButton.style.color = "#aaa";
                                applyButton.style.filter = "blur(1px)";

                                requestAnimationFrame(() => {
                                    // Slide up with glow effect and color mixing
                                    applyButton.style.transition =
                                        "all 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275)";
                                    applyButton.style.transform =
                                        "translateY(-50%) translateY(-2px)";
                                    applyButton.style.opacity = "0.9";
                                    applyButton.style.color = "#999";
                                    applyButton.style.filter =
                                        "blur(0px) drop-shadow(0 0 6px rgba(153, 153, 153, 0.8)) hue-rotate(20deg)";
                                    applyButton.style.textShadow =
                                        "0 0 10px rgba(200, 200, 200, 0.9)";

                                    // Phase 5: Settle with subtle bounce and color normalization
                                    setTimeout(() => {
                                        // Check if user made changes during animation
                                        const currentChanges =
                                            filenameTemplate !== "" &&
                                            filenameTemplate !==
                                            (localStorage.getItem("filenameTemplate") || "{name}");

                                        applyButton.style.transition = "all 150ms ease-out";
                                        applyButton.style.transform =
                                            "translateY(-50%) translateY(0px)";

                                        if (currentChanges) {
                                            // Show green if changes exist
                                            applyButton.style.opacity = "1";
                                            applyButton.style.color = "#4CAF50";
                                            applyButton.style.filter =
                                                "drop-shadow(0 0 4px rgba(76, 175, 80, 0.6))";
                                        } else {
                                            // Show gray if no changes
                                            applyButton.style.opacity = "0.5";
                                            applyButton.style.color = "#666";
                                            applyButton.style.filter = "none";
                                        }
                                        applyButton.style.textShadow = "none";

                                        // Reset input box border after delay
                                        setTimeout(() => {
                                            templateInput.classList.remove("applying-changes");
                                            if (document.activeElement === templateInput) {
                                                templateInput.style.borderColor = "#888";
                                                templateInput.style.boxShadow =
                                                    "inset 0 0 0 1px rgba(136, 136, 136, 0.3)";
                                            } else {
                                                templateInput.style.borderColor = "#555";
                                                templateInput.style.boxShadow = "none";
                                            }
                                        }, 100);

                                        // Clean up Applied! text
                                        appliedText.remove();
                                        isAnimating = false;
                                    }, 250);
                                });
                            }, 100);
                        }, 500); // Shorter stay time for snappier feel
                    }, 120);
                }, 50);
            };

            const startGrayHold = () => {
                if (!hasChanges && !isAnimating) {
                    // Hold down effect for gray checkmark
                    applyButton.style.transition = "all 100ms ease";
                    applyButton.style.transform = "translateY(-50%) scale(0.9)";
                    applyButton.style.opacity = "0.3";
                }
            };

            const endGrayHold = () => {
                if (!hasChanges && !isAnimating) {
                    // Release effect for gray checkmark
                    applyButton.style.transition = "all 100ms ease";
                    applyButton.style.transform = "translateY(-50%) scale(1)";
                    applyButton.style.opacity = "0.5";
                }
            };

            const handleGrayClick = () => {
                if (!hasChanges && !isAnimating) {
                    startGrayHold();
                    setTimeout(() => endGrayHold(), 100);
                }
            };

            applyButton.addEventListener("mousedown", () => {
                if (hasChanges && !isAnimating) {
                    // Green glow for input box on click too (slightly thicker but subtle)
                    templateInput.classList.add("applying-changes");
                    templateInput.style.borderColor = "#4CAF50";
                    templateInput.style.boxShadow = "0 0 8px rgba(76, 175, 80, 0.5)";
                    performApplyAction();
                } else if (!hasChanges && !isAnimating) {
                    isMouseHeld = true;
                    startGrayHold();
                }
            });

            applyButton.addEventListener("mouseup", () => {
                if (isMouseHeld) {
                    isMouseHeld = false;
                    endGrayHold();
                }
            });

            applyButton.addEventListener("mouseleave", () => {
                if (isMouseHeld) {
                    isMouseHeld = false;
                    endGrayHold();
                }
            });

            // Set initial apply button state
            if (hasChanges) {
                applyButton.style.opacity = "1";
                applyButton.style.color = "#4CAF50";
            }

            templateInputContainer.appendChild(applyButton);
            templateSection.appendChild(templateInputContainer);

            const tokenButtonsContainer = makeElement(
                "div", {}, {
                    display: "flex",
                    flexWrap: "wrap",
                    gap: "4px",
                    marginBottom: "8px",
                    alignItems: "center",
                },
            );

            // Create token buttons
            const tokens = [{
                    token: "id",
                    label: "ID"
                },
                {
                    token: "creator",
                    label: "Creator"
                },
                {
                    token: "name",
                    label: "Name"
                },
                {
                    token: "chat_name",
                    label: "Chat Name"
                },
                {
                    token: "created",
                    label: "Created"
                },
                {
                    token: "updated",
                    label: "Updated"
                },
                {
                    token: "tags",
                    label: "Tags"
                },
            ];

            tokens.forEach(({
                token,
                label
            }) => {
                const button = createTokenButton(token, label, templateInput, false);
                tokenButtonsContainer.appendChild(button);
            });

            templateSection.appendChild(tokenButtonsContainer);

            // Save Path Section
            const savePathSection = makeElement(
                "div", {}, {
                    marginTop: "20px",
                    marginBottom: "15px",
                    padding: "15px",
                    background: "#2a2a2a",
                    borderRadius: "10px",
                    boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                    transition: "all 200ms ease",
                    cursor: "default",
                },
            );

            const savePathTitle = makeElement(
                "h3", {
                    textContent: "Save Path",
                }, {
                    margin: "0 0 15px 0",
                    fontSize: "16px",
                    color: "#fff",
                    fontWeight: "bold",
                    textAlign: "center",
                },
            );
            savePathSection.appendChild(savePathTitle);

            const savePathInputContainer = makeElement(
                "div", {
                    className: "savepath-input-container",
                }, {
                    marginBottom: "12px",
                    position: "relative",
                    overflow: "hidden",
                },
            );

            // Update userChangedSavePath to current state
            userChangedSavePath =
                localStorage.getItem("userChangedSavePath") === "true";

            // Always use default prefill if user hasn't applied changes yet
            let savePathTemplate = userChangedSavePath ?
                localStorage.getItem("savePathTemplate") || "" :
                "cards/{creator}";
            const savePathInput = makeElement(
                "input", {
                    type: "text",
                    value: savePathTemplate,
                    placeholder: "Leave empty to save to the root directory",
                }, {
                    width: "100%",
                    padding: "10px 55px 10px 12px",
                    border: "1px solid #555",
                    borderRadius: "6px",
                    background: "#1a1a1a",
                    color: "#fff",
                    fontSize: "13px",
                    boxSizing: "border-box",
                    transition: "border-color 200ms ease, box-shadow 200ms ease",
                    outline: "none",
                },
            );

            // Disable tooltips
            savePathInput.setAttribute("title", "");
            savePathInput.setAttribute("data-tooltip-disabled", "true");
            savePathInput.removeAttribute("title");
            savePathInput.addEventListener("mouseenter", (e) => {
                e.target.removeAttribute("title");
                e.target.title = "";
            });

            // Override browser tooltip styles
            savePathInput.style.setProperty("pointer-events", "auto", "important");
            savePathInput.addEventListener("mouseover", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });

            let originalSavePathTemplate = userChangedSavePath ?
                localStorage.getItem("savePathTemplate") || "" :
                "cards/{creator}";
            let savePathHasChanges = savePathTemplate !== originalSavePathTemplate;

            // Auto-restore functionality removed for Save Path

            savePathInput.addEventListener("input", (e) => {
                savePathTemplate = e.target.value;
                // Update userChangedSavePath state
                userChangedSavePath =
                    localStorage.getItem("userChangedSavePath") === "true";
                const currentOriginal = userChangedSavePath ?
                    localStorage.getItem("savePathTemplate") || "" :
                    "cards/{creator}";
                savePathHasChanges = savePathTemplate !== currentOriginal;

                // Update apply button state
                const applyBtn = savePathSection.querySelector(".savepath-apply-btn");
                if (applyBtn && !savePathIsAnimating) {
                    applyBtn.style.transition = "all 200ms ease";
                    if (savePathHasChanges) {
                        applyBtn.style.opacity = "1";
                        applyBtn.style.color = "#4CAF50";
                        applyBtn.style.filter = "drop-shadow(0 0 4px rgba(76, 175, 80, 0.6))";
                    } else {
                        applyBtn.style.opacity = "0.5";
                        applyBtn.style.color = "#666";
                        applyBtn.style.filter = "none";
                    }
                }
            });

            // restoreSavePath(); // Removed auto-restore for Save Path

            let savePathIsEnterHeld = false;
            let savePathIsMouseHeld = false;
            let savePathIsAnimating = false;

            savePathInput.addEventListener("keydown", (e) => {
                if (e.key === "Enter" && !savePathIsEnterHeld) {
                    savePathIsEnterHeld = true;
                    e.preventDefault();
                    if (savePathHasChanges && !savePathIsAnimating) {
                        performSavePathApplyAction();
                    } else if (!savePathHasChanges && !savePathIsAnimating) {
                        startSavePathGrayHold();
                    }
                }
            });

            savePathInput.addEventListener("keyup", (e) => {
                if (e.key === "Enter") {
                    savePathIsEnterHeld = false;
                    if (!savePathHasChanges && !savePathIsAnimating) {
                        endSavePathGrayHold();
                    }
                }
            });

            savePathInput.addEventListener("mouseover", () => {
                if (
                    !savePathIsAnimating &&
                    document.activeElement !== savePathInput &&
                    savePathInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    savePathInput.style.borderColor = "#777";
                }
            });

            savePathInput.addEventListener("mouseout", () => {
                if (
                    !savePathIsAnimating &&
                    document.activeElement !== savePathInput &&
                    savePathInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    savePathInput.style.borderColor = "#555";
                }
            });

            savePathInput.addEventListener("focus", () => {
                if (
                    !savePathIsAnimating &&
                    savePathInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    savePathInput.style.borderColor = "#888";
                }
            });

            savePathInput.addEventListener("blur", () => {
                if (
                    !savePathIsAnimating &&
                    savePathInput.style.borderColor !== "rgb(76, 175, 80)"
                ) {
                    savePathInput.style.borderColor = "#555";
                }
            });

            savePathInputContainer.appendChild(savePathInput);

            const savePathApplyButton = makeElement(
                "button", {
                    textContent: "✓",
                    className: "savepath-apply-btn",
                }, {
                    position: "absolute",
                    right: "8px",
                    top: "50%",
                    transform: "translateY(-50%)",
                    background: "transparent",
                    border: "none",
                    color: "#666",
                    fontSize: "14px",
                    cursor: "pointer",
                    padding: "4px",
                    borderRadius: "3px",
                    opacity: "0.5",
                    transition: "all 200ms ease",
                },
            );

            const performSavePathApplyAction = () => {
                if (!savePathHasChanges || savePathIsAnimating) return;
                savePathIsAnimating = true;

                savePathInput.classList.add("applying-changes");
                savePathInput.style.borderColor = "#4CAF50";
                savePathInput.style.boxShadow = "0 0 8px rgba(76, 175, 80, 0.5)";

                savePathApplyButton.style.color = "#2ecc71";
                savePathApplyButton.style.filter =
                    "drop-shadow(0 0 8px rgba(46, 204, 113, 0.8))";
                savePathApplyButton.style.textShadow = "0 0 12px rgba(46, 204, 113, 0.9)";

                setTimeout(() => {
                    localStorage.setItem("savePathTemplate", savePathTemplate);
                    // Set boolean to true when user applies any changes to save path
                    localStorage.setItem("userChangedSavePath", "true");
                    userChangedSavePath = true; // Update the variable immediately
                    originalSavePathTemplate = savePathTemplate;
                    savePathHasChanges = false;

                    savePathApplyButton.style.transition =
                        "all 180ms cubic-bezier(0.25, 0.46, 0.45, 0.94)";
                    savePathApplyButton.style.transform =
                        "translateY(-50%) translateY(-30px)";
                    savePathApplyButton.style.filter = "blur(4px)";
                    savePathApplyButton.style.opacity = "0";

                    setTimeout(() => {
                        const appliedText = makeElement(
                            "span", {
                                textContent: "Applied!",
                            }, {
                                position: "absolute",
                                right: "8px",
                                top: "50%",
                                transform: "translateY(-50%) translateY(25px)",
                                color: "#4CAF50",
                                fontSize: "11px",
                                fontWeight: "bold",
                                opacity: "0",
                                transition: "all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94)",
                                pointerEvents: "none",
                                whiteSpace: "nowrap",
                            },
                        );

                        savePathInputContainer.appendChild(appliedText);

                        requestAnimationFrame(() => {
                            appliedText.style.opacity = "1";
                            appliedText.style.transform = "translateY(-50%) translateY(0px)";
                            appliedText.style.filter = "blur(0px)";
                        });

                        setTimeout(() => {
                            appliedText.style.transition =
                                "all 120ms cubic-bezier(0.25, 0.46, 0.45, 0.94)";
                            appliedText.style.transform = "translateY(-50%) translateY(-25px)";
                            appliedText.style.filter = "blur(3px)";
                            appliedText.style.opacity = "0";

                            setTimeout(() => {
                                savePathApplyButton.style.transition = "none";
                                savePathApplyButton.style.transform =
                                    "translateY(-50%) translateY(25px)";
                                savePathApplyButton.style.opacity = "0";
                                savePathApplyButton.style.color = "#aaa";
                                savePathApplyButton.style.filter = "blur(1px)";

                                requestAnimationFrame(() => {
                                    savePathApplyButton.style.transition =
                                        "all 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275)";
                                    savePathApplyButton.style.transform =
                                        "translateY(-50%) translateY(-2px)";
                                    savePathApplyButton.style.opacity = "0.9";
                                    savePathApplyButton.style.color = "#999";
                                    savePathApplyButton.style.filter =
                                        "blur(0px) drop-shadow(0 0 6px rgba(153, 153, 153, 0.8)) hue-rotate(20deg)";
                                    savePathApplyButton.style.textShadow =
                                        "0 0 10px rgba(200, 200, 200, 0.9)";

                                    setTimeout(() => {
                                        // Always set to gray state after applying since changes are now saved
                                        savePathApplyButton.style.transition = "all 150ms ease-out";
                                        savePathApplyButton.style.transform =
                                            "translateY(-50%) translateY(0px)";
                                        savePathApplyButton.style.opacity = "0.5";
                                        savePathApplyButton.style.color = "#666";
                                        savePathApplyButton.style.filter = "none";
                                        savePathApplyButton.style.textShadow = "none";

                                        setTimeout(() => {
                                            savePathInput.classList.remove("applying-changes");
                                            if (document.activeElement === savePathInput) {
                                                savePathInput.style.borderColor = "#888";
                                                savePathInput.style.boxShadow =
                                                    "inset 0 0 0 1px rgba(136, 136, 136, 0.3)";
                                            } else {
                                                savePathInput.style.borderColor = "#555";
                                                savePathInput.style.boxShadow = "none";
                                            }
                                        }, 100);

                                        appliedText.remove();
                                        savePathIsAnimating = false;
                                    }, 250);
                                });
                            }, 100);
                        }, 500);
                    }, 120);
                }, 50);
            };

            const startSavePathGrayHold = () => {
                if (!savePathHasChanges && !savePathIsAnimating) {
                    savePathApplyButton.style.transition = "all 100ms ease";
                    savePathApplyButton.style.transform = "translateY(-50%) scale(0.9)";
                    savePathApplyButton.style.opacity = "0.3";
                }
            };

            const endSavePathGrayHold = () => {
                if (!savePathHasChanges && !savePathIsAnimating) {
                    savePathApplyButton.style.transition = "all 100ms ease";
                    savePathApplyButton.style.transform = "translateY(-50%) scale(1)";
                    savePathApplyButton.style.opacity = "0.5";
                }
            };

            savePathApplyButton.addEventListener("mousedown", () => {
                if (savePathHasChanges && !savePathIsAnimating) {
                    savePathInput.classList.add("applying-changes");
                    savePathInput.style.borderColor = "#4CAF50";
                    savePathInput.style.boxShadow = "0 0 8px rgba(76, 175, 80, 0.5)";
                    performSavePathApplyAction();
                } else if (!savePathHasChanges && !savePathIsAnimating) {
                    savePathIsMouseHeld = true;
                    startSavePathGrayHold();
                }
            });

            savePathApplyButton.addEventListener("mouseup", () => {
                if (savePathIsMouseHeld) {
                    savePathIsMouseHeld = false;
                    endSavePathGrayHold();
                }
            });

            savePathApplyButton.addEventListener("mouseleave", () => {
                if (savePathIsMouseHeld) {
                    savePathIsMouseHeld = false;
                    endSavePathGrayHold();
                }
            });

            if (savePathHasChanges) {
                savePathApplyButton.style.opacity = "1";
                savePathApplyButton.style.color = "#4CAF50";
            }

            savePathInputContainer.appendChild(savePathApplyButton);
            savePathSection.appendChild(savePathInputContainer);

            const savePathTokenButtonsContainer = makeElement(
                "div", {}, {
                    display: "flex",
                    flexWrap: "wrap",
                    gap: "4px",
                    marginBottom: "8px",
                    alignItems: "center",
                },
            );

            tokens.forEach(({
                token,
                label
            }) => {
                const button = createTokenButton(token, label, savePathInput, true);
                savePathTokenButtonsContainer.appendChild(button);
            });

            savePathSection.appendChild(savePathTokenButtonsContainer);

            // FileSystemAccess API Toggle
            const useFileSystemToggle = createToggle(
                "useFileSystemAccess",
                "Use FileSystemAccess API",
                "Enable to use save path with directory picker. Disable for regular downloads.",
                localStorage.getItem("useFileSystemAccess") === null ? false : localStorage.getItem("useFileSystemAccess") === "true",
            );

            useFileSystemToggle.input.addEventListener("change", (e) => {
                const isEnabled = e.target.checked;
                localStorage.setItem("useFileSystemAccess", isEnabled);

                // Smooth animation with resume logic
                directorySection.style.transition =
                    "all 400ms cubic-bezier(0.4, 0, 0.2, 1)";

                if (isEnabled) {
                    // Slide down animation
                    directorySection.style.maxHeight = "35px";
                    directorySection.style.opacity = "1";
                    directorySection.style.paddingTop = "5px";
                    directorySection.style.paddingBottom = "5px";
                    directorySection.style.marginBottom = "5px";
                    directorySection.style.transform = "translateY(0)";
                } else {
                    // Slide up animation
                    directorySection.style.maxHeight = "0";
                    directorySection.style.opacity = "0";
                    directorySection.style.paddingTop = "0";
                    directorySection.style.paddingBottom = "0";
                    directorySection.style.marginBottom = "0";
                    directorySection.style.transform = "translateY(-10px)";
                }
            });

            savePathSection.appendChild(useFileSystemToggle.container);

            // Directory Picker with smooth animation support
            const directorySection = makeElement(
                "div", {}, {
                    marginTop: "5px",
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "space-between",
                    width: "100%",
                    overflow: "hidden",
                    transition: "all 400ms cubic-bezier(0.4, 0, 0.2, 1)",
                    maxHeight: localStorage.getItem("useFileSystemAccess") === "true" ? "35px" : "0",
                    opacity: localStorage.getItem("useFileSystemAccess") === "true" ? "1" : "0",
                    paddingTop: localStorage.getItem("useFileSystemAccess") === "true" ? "5px" : "0",
                    paddingBottom: localStorage.getItem("useFileSystemAccess") === "true" ? "5px" : "0",
                    marginBottom: localStorage.getItem("useFileSystemAccess") === "true" ? "5px" : "0",
                },
            );

            const directoryLabel = makeElement(
                "span", {
                    textContent: "Directory",
                }, {
                    fontSize: "13px",
                    color: "#fff",
                    fontWeight: "bold",
                },
            );

            const directoryRightSection = makeElement(
                "div", {}, {
                    display: "flex",
                    alignItems: "center",
                    gap: "10px",
                },
            );

            const directoryName = makeElement(
                "span", {
                    textContent: "Not selected",
                }, {
                    fontSize: "12px",
                    color: "#aaa",
                    fontStyle: "italic",
                },
            );

            const directoryButton = makeElement(
                "button", {
                    textContent: "Browse",
                    className: "directory-button",
                }, {
                    background: "#333",
                    color: "#fff",
                    border: "none",
                    borderRadius: "6px",
                    padding: "8px 16px",
                    cursor: "pointer",
                    fontSize: "12px",
                    transition: "all 200ms ease",
                },
            );

            let selectedDirectory = null;

            // IndexedDB functions for directory handle persistence
            const DB_NAME = "DirectoryHandles";
            const DB_VERSION = 1;
            const STORE_NAME = "handles";

            async function storeDirectoryHandle(handle) {
                return new Promise((resolve, reject) => {
                    const request = indexedDB.open(DB_NAME, DB_VERSION);

                    request.onupgradeneeded = (e) => {
                        const db = e.target.result;
                        if (!db.objectStoreNames.contains(STORE_NAME)) {
                            db.createObjectStore(STORE_NAME);
                        }
                    };

                    request.onsuccess = (e) => {
                        const db = e.target.result;
                        const transaction = db.transaction([STORE_NAME], "readwrite");
                        const store = transaction.objectStore(STORE_NAME);

                        const putRequest = store.put(handle, "selectedDirectory");
                        putRequest.onsuccess = () => resolve();
                        putRequest.onerror = () => reject(putRequest.error);
                    };

                    request.onerror = () => reject(request.error);
                });
            }

            async function getDirectoryHandle() {
                return new Promise((resolve, reject) => {
                    const request = indexedDB.open(DB_NAME, DB_VERSION);

                    request.onupgradeneeded = (e) => {
                        const db = e.target.result;
                        if (!db.objectStoreNames.contains(STORE_NAME)) {
                            db.createObjectStore(STORE_NAME);
                        }
                    };

                    request.onsuccess = (e) => {
                        const db = e.target.result;
                        const transaction = db.transaction([STORE_NAME], "readonly");
                        const store = transaction.objectStore(STORE_NAME);

                        const getRequest = store.get("selectedDirectory");
                        getRequest.onsuccess = () => resolve(getRequest.result);
                        getRequest.onerror = () => reject(getRequest.error);
                    };

                    request.onerror = () => reject(request.error);
                });
            }

            // Restore directory selection on page load
            directoryName.textContent = "Not selected";
            directoryName.style.color = "#aaa";
            directoryName.style.fontStyle = "italic";

            // Async function to restore directory handle
            (async () => {
                try {
                    const storedHandle = await getDirectoryHandle();
                    if (storedHandle) {
                        // Test if the handle is still valid
                        try {
                            const permission = await storedHandle.queryPermission({
                                mode: "readwrite",
                            });
                            if (permission === "granted" || permission === "prompt") {
                                selectedDirectory = storedHandle;
                                window.selectedDirectoryHandle = storedHandle;
                                directoryName.textContent = storedHandle.name;
                                directoryName.style.color = "#4CAF50";
                                directoryName.style.fontStyle = "normal";
                                debugLog(
                                    "[Directory] Restored from IndexedDB:",
                                    storedHandle.name,
                                );
                            } else {
                                debugLog("[Directory] Handle exists but permission denied");
                            }
                        } catch (error) {
                            debugLog("[Directory] Handle invalid, removing from storage");
                            // Handle is invalid, remove it
                            const request = indexedDB.open(DB_NAME, DB_VERSION);
                            request.onsuccess = (e) => {
                                const db = e.target.result;
                                const transaction = db.transaction([STORE_NAME], "readwrite");
                                const store = transaction.objectStore(STORE_NAME);
                                store.delete("selectedDirectory");
                            };
                        }
                    }
                } catch (error) {
                    debugLog("[Directory] Error restoring from IndexedDB:", error);
                }
            })();

            directoryButton.addEventListener("click", async () => {
                if ("showDirectoryPicker" in window) {
                    try {
                        selectedDirectory = await window.showDirectoryPicker();
                        window.selectedDirectoryHandle = selectedDirectory; // Store globally for save function
                        directoryName.textContent = selectedDirectory.name;
                        directoryName.style.color = "#4CAF50";
                        directoryName.style.fontStyle = "normal";

                        // Persist directory handle in IndexedDB
                        storeDirectoryHandle(selectedDirectory)
                            .then(() => {
                                debugLog(
                                    "[Directory] Stored in IndexedDB:",
                                    selectedDirectory.name,
                                );
                            })
                            .catch((error) => {
                                debugLog("[Directory] Failed to store in IndexedDB:", error);
                            });
                    } catch (err) {
                        debugLog("Directory picker was cancelled or failed:", err);
                    }
                } else {
                    alert("FileSystemAccess API is not supported in this browser.");
                }
            });

            directoryRightSection.appendChild(directoryName);
            directoryRightSection.appendChild(directoryButton);
            directorySection.appendChild(directoryLabel);
            directorySection.appendChild(directoryRightSection);
            savePathSection.appendChild(directorySection);

            settingsContent.appendChild(savePathSection);
            settingsContent.appendChild(templateSection);

            // Export Options Section
            const exportOptionsSection = makeElement(
                "div", {}, {
                    marginTop: "20px",
                    marginBottom: "15px",
                    padding: "15px",
                    background: "#2a2a2a",
                    borderRadius: "10px",
                    boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                    transition: "all 200ms ease",
                    cursor: "default",
                },
            );

            const exportOptionsTitle = makeElement(
                "h3", {
                    textContent: "Export Options",
                }, {
                    margin: "0 0 15px 0",
                    fontSize: "16px",
                    color: "#fff",
                    fontWeight: "bold",
                },
            );
            exportOptionsSection.appendChild(exportOptionsTitle);

            // Use character's chat name toggle
            const chatNameToggle = createToggle(
                "useChatNameForName",
                "Use chat name",
                "Uses chat name for the character name instead of the card's main name.",
                useChatNameForName,
            );
            chatNameToggle.input.addEventListener("change", (e) => {
                useChatNameForName = e.target.checked;
            });
            exportOptionsSection.appendChild(chatNameToggle.container);

            // Apply {{char}} tokenization toggle
            // Apply {{char}} tokenization toggle
            const charTokenToggle = createToggle(
                "applyCharToken",
                "Tokenize char name",
                "Replaces the character's name with {{char}}.",
                applyCharToken,
            );
            charTokenToggle.input.addEventListener("change", (e) => {
                applyCharToken = e.target.checked;
            });
            exportOptionsSection.appendChild(charTokenToggle.container);

            // Include Creator Notes toggle
            const creatorNotesToggle = createToggle(
                "includeCreatorNotes",
                "Include creator notes",
                "Include the creator's notes in exported files.",
                localStorage.getItem("includeCreatorNotes") === null ? true : localStorage.getItem("includeCreatorNotes") !== "false",
            );
            exportOptionsSection.appendChild(creatorNotesToggle.container);

            // Include Tags toggle
            const includeTagsToggle = createToggle(
                "includeTags",
                "Include tags",
                "Include character tags in exported files (PNG/JSON only). Emojis will be removed.",
                localStorage.getItem("includeTags") === null ? false : localStorage.getItem("includeTags") !== "false",
            );
            exportOptionsSection.appendChild(includeTagsToggle.container);

            settingsContent.appendChild(exportOptionsSection);

            // Advanced Section
            const advancedSection = makeElement(
                "div", {}, {
                    marginTop: "20px",
                    marginBottom: "15px",
                    padding: "15px",
                    background: "#2a2a2a",
                    borderRadius: "10px",
                    boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                    transition: "all 200ms ease",
                    cursor: "default",
                },
            );

            const advancedTitle = makeElement(
                "h3", {
                    textContent: "Advanced",
                }, {
                    margin: "0 0 15px 0",
                    fontSize: "16px",
                    color: "#fff",
                    fontWeight: "bold",
                },
            );
            advancedSection.appendChild(advancedTitle);

            // Show Debug Logs toggle
            const debugLogsToggle = createToggle(
                "showDebugLogs",
                "Debug logs",
                "Show verbose console logging for debugging",
                localStorage.getItem("showDebugLogs") === null ? false : localStorage.getItem("showDebugLogs") === "true",
            );
            advancedSection.appendChild(debugLogsToggle.container);

            settingsContent.appendChild(advancedSection);

            const tabs = {
                export: {
                    content: exportContent,
                    tab: exportTab,
                    active: true,
                },
                settings: {
                    content: settingsContent,
                    tab: settingsTab,
                    active: false,
                },
            };

            function switchTab(tabKey) {
                const previousTab = currentActiveTab;
                currentActiveTab = tabKey;

                // Save filename draft and clean up toggle states when switching AWAY from settings
                if (previousTab === "settings" && tabKey !== "settings") {
                    const templateInput = document.querySelector(
                        'input[placeholder="Enter filename template"]',
                    );
                    if (templateInput && templateInput.value) {
                        localStorage.setItem("filenameTemplateDraft", templateInput.value);
                    }
                }


                // Only restore when switching TO settings (not from settings)
                if (tabKey === "settings" && previousTab !== "settings") {
                    // Immediate restoration before settings tab loads
                    inputStateManager.restoreAll(false);

                    // Use requestAnimationFrame to ensure DOM is ready for draft restoration
                    requestAnimationFrame(() => {
                        const templateInput = document.querySelector(
                            'input[placeholder="Enter filename template"]',
                        );
                        if (templateInput) {
                            const savedDraft = localStorage.getItem("filenameTemplateDraft");
                            if (savedDraft && savedDraft !== templateInput.value) {
                                templateInput.value = savedDraft;
                                filenameTemplate = savedDraft;
                                templateInput.dispatchEvent(
                                    new Event("input", {
                                        bubbles: true
                                    }),
                                );
                            }
                        }

                        // Auto-restore removed for Save Path input
                    });
                }

                // // Clean up scrollbar when switching AWAY from settings
                // if (previousTab === "settings" && tabKey !== "settings") {
                //     if (settingsScrollbar && settingsScrollbar.destroy) {
                //         settingsScrollbar.destroy();
                //         settingsScrollbar = null;
                //     }
                //     // Clean up any remaining scrollbar elements
                //     const existingTrack = settingsContent.parentElement?.querySelector(
                //         ".custom-scrollbar-track",
                //     );
                //     if (existingTrack) {
                //         existingTrack.remove();
                //     }
                // }

                animationTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
                animationTimeouts = [];

                Object.entries(tabs).forEach(([key, {
                    content,
                    tab
                }]) => {
                    const isActive = key === tabKey;

                    tab.style.opacity = isActive ? "1" : "0.7";
                    tab.style.transform = isActive ? "translateY(-2px)" : "";

                    const indicator = tab.lastChild;
                    if (indicator) {
                        if (isActive) {
                            indicator.style.background = ACTIVE_TAB_COLOR;
                            indicator.style.transform = "scaleX(1)";
                        } else {
                            indicator.style.background = INACTIVE_TAB_COLOR;
                            indicator.style.transform = "scaleX(0.5)";
                        }
                    }

                    content.style.display = "block";
                    content.style.pointerEvents = isActive ? "auto" : "none";

                    if (isActive) {
                        content.style.opacity = "0";
                        content.style.transform = "scale(0.95)";

                        void content.offsetWidth;

                        requestAnimationFrame(() => {
                            content.style.opacity = "1";
                            content.style.transform = "scale(1)";

                            // Show scrollbar when settings tab becomes active
                            if (key === "settings") {
                                settingsScrollbar.show();
                            }
                        });
                    } else {
                        requestAnimationFrame(() => {
                            content.style.opacity = "0";
                            content.style.transform = "scale(0.95)";

                            // Hide scrollbar when settings tab becomes inactive
                            if (key === "settings") {
                                settingsScrollbar.hide();
                            }
                        });


                        const hideTimeout = setTimeout(() => {
                            if (!tabs[key].active) {
                                content.style.display = "none";
                            }
                        }, TAB_ANIMATION_DURATION);


                        animationTimeouts.push(hideTimeout);
                    }

                    tabs[key].active = isActive;
                });

                currentTab = tabKey;
                try {
                    sessionStorage.setItem("lastActiveTab", tabKey);
                } catch (e) {
                    debugWarn("Failed to save tab state to sessionStorage", e);
                }
            }

            const handleTabClick = (e) => {
                const tt = document.getElementById("char-export-tooltip");
                if (tt) {
                    tt.style.opacity = "0";
                    const offsetHide = TOOLTIP_SLIDE_FROM_RIGHT ?
                        -TOOLTIP_SLIDE_OFFSET :
                        TOOLTIP_SLIDE_OFFSET;
                    tt.style.transform = `translateX(${offsetHide}px)`;
                }
                const tabKey = e.target === exportTab ? "export" : "settings";
                if (!tabs[tabKey].active) {
                    switchTab(tabKey);
                }
            };

            exportTab.onclick = handleTabClick;
            settingsTab.onclick = handleTabClick;

            Object.entries(tabs).forEach(([key, {
                content
            }]) => {
                const isActive = key === currentTab;

                content.style.display = isActive ? "block" : "none";
                content.style.opacity = isActive ? "1" : "0";
                content.style.transform = isActive ? "scale(1)" : "scale(0.95)";
            });

            switchTab(currentTab);

            // Show scrollbar if settings tab is initially active
            if (currentTab === "settings") {
                settingsScrollbar.show();
            }
            document.body.appendChild(gui);

            // Add backdrop with smooth linear animation
            const backdrop = makeElement(
                "div", {
                    id: "char-export-backdrop",
                }, {
                    position: "fixed",
                    top: "0",
                    left: "0",
                    width: "100%",
                    height: "100%",
                    background: "rgba(0, 0, 0, 0)",
                    backdropFilter: "blur(0px)",
                    zIndex: "9999",
                    opacity: "0",
                    transition: "all 150ms ease-out",
                },
            );

            document.body.insertBefore(backdrop, gui);

            // Set initial modal state
            gui.style.opacity = "0";
            gui.style.transform = "translate(-50%, -50%) scale(0.95)";
            gui.style.filter = "blur(3px)";
            gui.style.transition = "all 150ms ease-out";

            // Start backdrop animation smoothly - no instant fill
            requestAnimationFrame(() => {
                backdrop.style.opacity = "1";
                backdrop.style.background = "rgba(0, 0, 0, 0.4)";
                backdrop.style.backdropFilter = "blur(3px)";

                gui.style.opacity = "1";
                gui.style.transform = "translate(-50%, -50%) scale(1)";
                gui.style.filter = "blur(0px) drop-shadow(0 15px 35px rgba(0,0,0,0.3))";
            });

            document.addEventListener("mousedown", handleDialogOutsideClick);
            document.addEventListener("mouseup", handleDialogOutsideClick);
        }

        function toggleUIState() {
            debugLog('[Debug] toggleUIState called, viewActive:', viewActive);
            animationTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
            animationTimeouts = [];

            if (guiElement && document.body.contains(guiElement)) {
                debugLog('[Debug] GUI element exists in document');
                if (viewActive) {
                    const backdrop = document.getElementById("char-export-backdrop");
                    const currentGuiOpacity =
                        parseFloat(getComputedStyle(guiElement).opacity) || 0;
                    const currentBackdropOpacity = backdrop ?
                        parseFloat(getComputedStyle(backdrop).opacity) || 0 :
                        0;

                    guiElement.style.display = "flex";

                    void guiElement.offsetHeight;

                    requestAnimationFrame(() => {
                        guiElement.style.transition = "all 150ms ease-out";
                        guiElement.style.opacity = "1";
                        guiElement.style.transform = "translate(-50%, -50%) scale(1)";
                        guiElement.style.filter =
                            "blur(0px) drop-shadow(0 15px 35px rgba(0,0,0,0.3))";

                        if (backdrop) {
                            backdrop.style.transition = "all 150ms ease-out";
                            backdrop.style.opacity = "1";
                            backdrop.style.background = "rgba(0, 0, 0, 0.4)";
                            backdrop.style.backdropFilter = "blur(3px)";
                        }
                    });
                } else {
                    const backdrop = document.getElementById("char-export-backdrop");

                    const currentGuiOpacity =
                        parseFloat(getComputedStyle(guiElement).opacity) || 1;
                    const currentBackdropOpacity = backdrop ?
                        parseFloat(getComputedStyle(backdrop).opacity) || 1 :
                        0;

                    const transformMatrix = getComputedStyle(guiElement).transform;
                    let currentScale = 1;
                    if (transformMatrix !== "none") {
                        const values = transformMatrix.match(/-?\d+\.?\d*/g);
                        if (values && values.length >= 6) {
                            currentScale = parseFloat(values[0]);
                        }
                    }

                    requestAnimationFrame(() => {
                        guiElement.style.transition = "all 120ms ease-in";
                        guiElement.style.opacity = "0";
                        guiElement.style.transform = "translate(-50%, -50%) scale(0.9)";
                        guiElement.style.filter = "blur(4px)";

                        if (backdrop) {
                            backdrop.style.transition = "all 120ms ease-in";
                            backdrop.style.opacity = "0";
                            backdrop.style.background = "rgba(0, 0, 0, 0)";
                            backdrop.style.backdropFilter = "blur(0px)";
                        }
                    });

                    const removeTimeout = setTimeout(() => {
                        if (!viewActive && guiElement && document.body.contains(guiElement)) {
                            document.body.removeChild(guiElement);
                            const backdrop = document.getElementById("char-export-backdrop");
                            if (backdrop) backdrop.remove();
                            document.removeEventListener("mousedown", handleDialogOutsideClick);
                            document.removeEventListener("mouseup", handleDialogOutsideClick);
                            guiElement = null;
                        }
                    }, 140);
                    animationTimeouts.push(removeTimeout);
                }
            } else if (viewActive) {
                createUI();
            }
        }

        /**
         * Centralized input state manager with immediate restoration
         * Handles DOM queries, variable sync, and immediate updates with retry logic
         * @param {boolean} force - Force restoration regardless of current value
         * @param {number} retryCount - Internal retry counter for DOM queries
         */
        function createInputStateManager() {
            const SELECTORS = {
                filename: 'input[placeholder="Enter filename template"]',
            };

            const DEFAULTS = {
                filename: "{name}",
            };

            const STATE_KEYS = {
                filename: "filenameTemplate",
            };

            function restoreInput(type, force = false, retryCount = 0) {
                const input = document.querySelector(SELECTORS[type]);

                if (!input) {
                    if (retryCount < 5) {
                        requestAnimationFrame(() =>
                            restoreInput(type, force, retryCount + 1),
                        );
                    }
                    return;
                }

                const savedValue =
                    localStorage.getItem(STATE_KEYS[type]) || DEFAULTS[type];
                const needsRestore = force || input.value.trim() === "";

                if (needsRestore) {
                    input.value = savedValue;
                    if (type === "filename") {
                        window.filenameTemplate = savedValue;
                        filenameTemplate = savedValue;
                    }

                    input.dispatchEvent(new Event("input", {
                        bubbles: true
                    }));
                }
            }

            return {
                restoreAll: (force = false) => {
                    if (restorationInProgress) return;
                    restorationInProgress = true;

                    restoreInput("filename", force);

                    setTimeout(() => {
                        restorationInProgress = false;
                    }, 50);
                },
                restoreFilename: (force = false) => restoreInput("filename", force),
            };
        }

        const inputStateManager = createInputStateManager();

        function closeV() {
            if (!viewActive) return;

            const templateInput = document.querySelector(
                'input[placeholder="Enter filename template"]',
            );
            debugLog('[Debug] Current chatData state:', {
                exists: !!chatData,
                character: chatData?.character,
                messages: chatData?.chatMessages?.length
            });

            if (templateInput && templateInput.value) {
                localStorage.setItem("filenameTemplateDraft", templateInput.value);
            }

            // Hide scrollbar but don't destroy it (it will be destroyed with the GUI)
            settingsScrollbar.hide();

            inputStateManager.restoreAll(false);

            requestAnimationFrame(() => {
                viewActive = false;
                toggleUIState();
            });
        }

        let isMouseDownInside = false;

        function handleDialogOutsideClick(e) {
            const gui = document.getElementById("char-export-gui");
            const backdrop = document.getElementById("char-export-backdrop");

            if (e.type === "mousedown") {
                isMouseDownInside = gui && gui.contains(e.target);
            } else if (e.type === "mouseup") {
                const isMouseUpOutside = !gui || !gui.contains(e.target);
                if (!isMouseDownInside && isMouseUpOutside) {
                    closeV();
                }
                isMouseDownInside = false;
            }
        }

        /* ============================
           ==      INTERCEPTORS     ==
           ============================ */
        function interceptNetwork() {
            debugLog('[Debug] interceptNetwork called, networkInterceptActive:', networkInterceptActive);
            if (networkInterceptActive) {
                debugLog('[Debug] Network intercept already active, returning');
                return;
            }
            networkInterceptActive = true;

            const origXHR = XMLHttpRequest.prototype.open;
            XMLHttpRequest.prototype.open = function(method, url) {
                this.addEventListener("load", () => {
                    if (url.includes("generateAlpha")) modifyResponse(this.responseText);
                    if (url.includes("/hampter/chats/"))
                        modifyChatResponse(this.responseText);
                });
                return origXHR.apply(this, arguments);
            };

            const origFetch = window.fetch;
            window.fetch = async function(...args) {
                const requestUrl = typeof args[0] === 'string' ? args[0] : args[0]?.url;

                if (shouldInterceptNext && cachedProxyUrl && requestUrl === cachedProxyUrl) {
                    return new Response('{"choices":[]}', {
                        status: 200,
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    });
                }

                try {
                    const res = await origFetch(...args);
                    if (res.ok) {
                        if (res.url?.includes("generateAlpha")) {
                            res.clone().text().then(modifyResponse).catch(() => {});
                        }
                        if (res.url?.includes("/hampter/chats/")) {
                            res.clone().text().then(modifyChatResponse).catch(() => {});
                        }
                    }
                    return res;
                } catch (error) {
                    return new Response(null, {
                        status: 500,
                        statusText: "Userscript Handled Fetch"
                    });
                }
            };
        }

        function modifyResponse(text) {
            debugLog('[Debug] modifyResponse called, shouldInterceptNext:', shouldInterceptNext);
            if (!shouldInterceptNext) {
                debugLog('[Debug] shouldInterceptNext is false, returning');
                return;
            }
            shouldInterceptNext = false;
            try {
                debugLog('[Debug] Attempting to parse response');
                const json = JSON.parse(text);
                const sys = json.messages.find((m) => m.role === "system")?.content || "";
                let initMsg = "";
                if (chatData?.chatMessages?.length) {
                    const msgs = chatData.chatMessages;
                    initMsg = msgs[msgs.length - 1].message;
                }
                const header = document.querySelector("p.chakra-text.css-1nj33dt");
                const headerName = header?.textContent
                    .match(/Chat with\s+(.*)$/)?.[1]
                    ?.trim();
                const fullName = (
                    chatData?.character?.chat_name ||
                    chatData?.character?.name ||
                    ""
                ).trim();
                const nameFirst = (chatData?.character?.name || "")
                    .trim()
                    .split(/\s+/)[0];
                const charName = fullName || nameFirst || headerName || "char";
                let charBlock = "";
                let scen = "";

                // --- Start of New Unified Parsing Logic ---

                // 1. Isolate the main definition content, removing all system/safety prefixes.
                let definitionContent = sys;
                const systemTagEnd = sys.indexOf("</system>");
                if (systemTagEnd !== -1) {
                    definitionContent = sys.substring(systemTagEnd + 9).trimStart();
                } else {
                    // Fallback for when </system> isn't present, remove known prefixes.
                    const safetyNoteEnd = sys.indexOf("\n[System note:");
                    if (safetyNoteEnd !== -1) {
                        definitionContent = sys.substring(safetyNoteEnd).trimStart();
                    }
                }

                const systemNoteMatch = definitionContent.match(/^\[System note:.*?\]\s*/);
                if (systemNoteMatch) {
                    definitionContent = definitionContent.substring(systemNoteMatch[0].length);
                }

                // 2. Find the boundary of the definition block (before UserPersona or example_dialogs).
                const userPersonaIndex = definitionContent.indexOf("<UserPersona>");
                const exampleDialogsIndex = definitionContent.indexOf("<example_dialogs>");
                let boundaryIndex = -1;

                if (userPersonaIndex !== -1 && exampleDialogsIndex !== -1) {
                    boundaryIndex = Math.min(userPersonaIndex, exampleDialogsIndex);
                } else {
                    boundaryIndex = Math.max(userPersonaIndex, exampleDialogsIndex);
                }

                const definitionBlock = (boundaryIndex !== -1 ?
                    definitionContent.substring(0, boundaryIndex) :
                    definitionContent
                ).trim();

                // 3. Split the block by the last newline to separate description and scenario.
                const lastNewline = definitionBlock.lastIndexOf('\n');

                if (lastNewline !== -1 && lastNewline < definitionBlock.length - 1) {
                    // If a newline exists and isn't the last character...
                    charBlock = definitionBlock.substring(0, lastNewline).trim();
                    scen = definitionBlock.substring(lastNewline + 1).trim();
                } else {
                    // If there's no newline, the whole block is the description.
                    charBlock = definitionBlock;
                    scen = ""; // No scenario found.
                }

                // --- End of New Unified Parsing Logic ---

                const rawExs = extractTagContent(sys, "example_dialogs");
                const exs = rawExs.replace(
                    /^\s*Example conversations between[^:]*:\s*/,
                    "",
                );
                const userName = cachedUserName;
                switch (exportFormat) {
                    case "txt": {
                        saveAsTxt(charBlock, scen, initMsg, exs, charName, userName);
                        break;
                    }
                    case "png": {
                        saveAsPng(charName, charBlock, scen, initMsg, exs, userName);
                        break;
                    }
                    case "json": {
                        saveAsJson(charName, charBlock, scen, initMsg, exs, userName);
                        break;
                    }
                }
                exportFormat = null;
            } catch (err) {
                debugError("Error processing response:", err);
            }
        }

        function modifyChatResponse(text) {
            try {
                if (!text || typeof text !== "string" || !text.trim()) return;

                const data = JSON.parse(text);
                if (data && data.character) {
                    chatData = data;
                }
            } catch (err) {
                // ignore parsing errors
            }
        }

        /* ============================
           ==      CORE LOGIC       ==
           ============================ */
        async function getCharacterMeta() {
            // ---------- BEGIN Method 1 helpers ----------
            const findCharacter = (obj, visited = new Set()) => {
                // Base case: If obj is not a searchable object or has already been visited, stop.
                if (!obj || typeof obj !== 'object' || visited.has(obj)) {
                    return null;
                }
                // Add the current object to the visited set to prevent infinite recursion.
                visited.add(obj);

                // Success condition: Check if the current object looks like a valid character object.
                // We use a combination of keys that are highly likely to be unique to the character definition.
                const isCharacterObject =
                    Object.prototype.hasOwnProperty.call(obj, "showdefinition") &&
                    Object.prototype.hasOwnProperty.call(obj, "id") &&
                    Object.prototype.hasOwnProperty.call(obj, "name") &&
                    Object.prototype.hasOwnProperty.call(obj, "personality") &&
                    Object.prototype.hasOwnProperty.call(obj, "first_message");

                if (isCharacterObject) {
                    return obj; // Found it!
                }

                // Recursive step: Iterate over all properties of the current object.
                for (const key in obj) {
                    // We only check own properties whose values are also objects.
                    if (Object.prototype.hasOwnProperty.call(obj, key) && typeof obj[key] === 'object') {
                        const result = findCharacter(obj[key], visited);
                        // If a nested call finds the character, propagate the result up immediately.
                        if (result) {
                            return result;
                        }
                    }
                }

                // Not found in this branch of the JSON tree.
                return null;
            };

            const extractFromJson = (json) => {
                if (!json) return null;
                const charObj = findCharacter(json);
                if (!charObj) {
                    if (localStorage.getItem("showDebugLogs") === "true") {
                        debugLog("[getCharacterMeta] Method 1: no character object found");
                    }
                    return null;
                }
                if (localStorage.getItem("showDebugLogs") === "true") {
                    debugLog(
                        "[getCharacterMeta] Method 1: character object located, showdefinition=",
                        charObj.showdefinition,
                    );
                }

                const {
                    name: rawName = "",
                    chat_name: chatName = "",
                    description: creatorNotesRaw = "",
                    creator = {},
                    personality: personalityRaw = "",
                    scenario: scenarioRaw = "",
                    first_message: firstMsgRaw = "",
                    example_dialogs: exDialogsRaw = "",
                    showdefinition = false,
                    id = "",
                } = charObj;

                if (!showdefinition) return null;

                const name = (rawName || chatName).trim();

                let characterVersion = getCharacterCardUrl(id);
                if (chatName && !useChatNameForName) {
                    characterVersion += `\nChat Name: ${chatName.trim()}`;
                }

                // Verbose logging for Method 1 (JSON.parse when showdefinition=true)
                if (localStorage.getItem("showDebugLogs") === "true") {
                    const src = "JSON.parse (showdefinition=true)";
                    debugLog("[getCharacterMeta] name", name, `<= ${src}`);
                    debugLog(
                        "[getCharacterMeta] personality",
                        personalityRaw,
                        `<= ${src}`,
                    );
                    debugLog("[getCharacterMeta] scenario", scenarioRaw, `<= ${src}`);
                    debugLog(
                        "[getCharacterMeta] first_message",
                        firstMsgRaw,
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] example_dialogs",
                        exDialogsRaw,
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] creator_notes",
                        creatorNotesRaw,
                        `<= ${src}`,
                    );
                }
                return {
                    characterVersion,
                    characterCardUrl: getCharacterCardUrl(id),
                    name,
                    creatorNotes: creatorNotesRaw,
                    personality: stripWatermark(personalityRaw),
                    scenario: stripWatermark(scenarioRaw),
                    firstMessage: stripWatermark(firstMsgRaw),
                    exampleDialogs: stripWatermark(exDialogsRaw),
                    definitionExposed: true,
                };
            };
            // ---------- END Method 1 helpers ----------
            // ---------- BEGIN Method 2 helpers (chatData fallback) ----------
            // Method 2 (showdefinition false) reads directly from chatData for character information.
            // ---------- END Method 2 helpers ----------
            const charId = chatData?.character?.id;
            if (!charId)
                return {
                    creatorUrl: "",
                    characterVersion: "",
                    characterCardUrl: "",
                    name: "",
                    creatorNotes: "",
                    personality: "",
                    scenario: "",
                    firstMessage: "",
                    exampleDialogs: "",
                    definitionExposed: false,
                };

            // Check cache first - return cached values unless debug logging is enabled
            const skipCache = localStorage.getItem("showDebugLogs") === "true";
            if (
                !skipCache &&
                characterMetaCache.id === charId &&
                characterMetaCache.useChatNameForName === useChatNameForName
            ) {
                if (localStorage.getItem("showDebugLogs") === "true") {
                    debugLog(
                        "[getCharacterMeta] Returning cached result for character:",
                        charId,
                    );
                }
                return {
                    creatorUrl: characterMetaCache.creatorUrl || "",
                    characterVersion: characterMetaCache.characterVersion || "",
                    characterCardUrl: characterMetaCache.characterCardUrl || "",
                    name: characterMetaCache.name || "",
                    creatorNotes: characterMetaCache.creatorNotes || "",
                    personality: characterMetaCache.personality || "",
                    scenario: characterMetaCache.scenario || "",
                    firstMessage: characterMetaCache.firstMessage || "",
                    exampleDialogs: characterMetaCache.exampleDialogs || "",
                    definitionExposed: characterMetaCache.definitionExposed || false,
                };
            }

            const characterCardUrl = getCharacterCardUrl(charId);
            let meta = {
                ...blankMeta,
                characterVersion: characterCardUrl,
                characterCardUrl,
            };
            try {
                const response = await fetch(characterCardUrl);
                const html = await response.text();
                const doc = new DOMParser().parseFromString(html, "text/html");
                // ---------- BEGIN Method 1 execution ----------
                let metaFromJson = null;
                try {
                    if (localStorage.getItem("showDebugLogs") === "true") {
                        debugLog("[getCharacterMeta] Method 1: scanning <script> tags");
                    }
                    const scripts = Array.from(doc.querySelectorAll("script"));
                    for (const s of scripts) {
                        const txt = s.textContent || "";
                        if (!txt.includes("window.mbxM.push(JSON.parse(")) continue;

                        // Start searching after the "JSON.parse(" part of the string.
                        const startIndex = txt.indexOf("JSON.parse(") + "JSON.parse(".length;
                        if (startIndex === "JSON.parse(".length - 1) continue;

                        let openParenCount = 1;
                        let endIndex = -1;

                        // Manually scan for the matching closing parenthesis to correctly
                        // capture the entire argument, even with nested structures.
                        for (let i = startIndex; i < txt.length; i++) {
                            if (txt[i] === '(') {
                                openParenCount++;
                            } else if (txt[i] === ')') {
                                openParenCount--;
                                if (openParenCount === 0) {
                                    endIndex = i;
                                    break;
                                }
                            }
                        }

                        if (endIndex === -1) continue;

                        // Extract the full argument string, e.g., '"{\\"key\\":\\"value\\"}"'
                        const jsonArgString = txt.substring(startIndex, endIndex).trim();

                        try {
                            // The site uses a double-encoded JSON string. We must parse it twice.
                            // 1. Parse the JavaScript string literal to get the raw JSON content.
                            const jsonContent = JSON.parse(jsonArgString);
                            // 2. Parse the raw JSON content to get the final JavaScript object.
                            const dataObject = JSON.parse(jsonContent);

                            // Now, search for the character object within the parsed data.
                            metaFromJson = extractFromJson(dataObject);
                            if (metaFromJson) {
                                break; // Success! Exit the loop.
                            }
                        } catch (e) {
                            if (localStorage.getItem("showDebugLogs") === "true") {
                                debugWarn("[getCharacterMeta] Failed to parse JSON from a script tag. Skipping.");
                            }
                            continue; // This JSON wasn't what we wanted, try the next script.
                        }
                    }
                } catch (parseErr) {
                    debugError(
                        "[getCharacterMeta] JSON parse error (Method 1):",
                        parseErr,
                    );
                }
                // ---------- END Method 1 execution ----------

                Object.assign(meta, metaFromJson || {}, {
                    creatorUrl: getCreatorUrlFromDoc(doc),
                    creatorNotes: chatData?.character?.description || "",
                });
            } catch (_) {}

            // Debug logging before cache assignment (shows every time when enabled)
            if (localStorage.getItem("showDebugLogs") === "true") {
                debugLog(
                    "[getCharacterMeta] creator_url",
                    meta.creatorUrl,
                    "<= getCreatorUrlFromDoc",
                );

                if (!meta.definitionExposed) {
                    const src = "generateAlpha/chatData (showdefinition=false)";
                    debugLog(
                        "[getCharacterMeta] name",
                        meta.name ||
                        chatData?.character?.name ||
                        chatData?.character?.chat_name,
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] personality",
                        chatData?.character?.personality || "extracted from generateAlpha",
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] scenario",
                        chatData?.character?.scenario || "extracted from generateAlpha",
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] first_message",
                        chatData?.character?.first_message || "extracted from generateAlpha",
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] example_dialogs",
                        chatData?.character?.example_dialogs ||
                        "extracted from generateAlpha",
                        `<= ${src}`,
                    );
                    debugLog(
                        "[getCharacterMeta] creator_notes",
                        chatData?.character?.description,
                        `<= ${src}`,
                    );
                }
            }

            Object.assign(characterMetaCache, {
                id: charId,
                useChatNameForName,
                ...meta,
            });

            return meta;
        }

        async function buildTemplate(charBlock, scen, initMsg, exs) {
            const sections = [];
            const {
                creatorUrl,
                characterCardUrl,
                creatorNotes
            } =
            await getCharacterMeta();
            const includeCreatorNotes =
                localStorage.getItem("includeCreatorNotes") === null ? true : localStorage.getItem("includeCreatorNotes") !== "false";

            const realName = chatData.character.name.trim();
            sections.push(`==== Name ====\n${realName}`);
            const chatName = (chatData.character.chat_name || realName).trim();
            sections.push(`==== Chat Name ====\n${chatName}`);
            if (charBlock) sections.push(`==== Description ====\n${charBlock.trim()}`);
            if (scen) sections.push(`==== Scenario ====\n${scen.trim()}`);
            if (initMsg) sections.push(`==== Initial Message ====\n${initMsg.trim()}`);
            if (exs) sections.push(`==== Example Dialogs ====\n${exs.trim()}`);
            sections.push(`==== Character Card ====\n${characterCardUrl}`);
            sections.push(`==== Creator ====\n${creatorUrl}`);
            if (includeCreatorNotes && creatorNotes)
                sections.push(`==== Creator Notes ====\n${creatorNotes}`);
            return sections.join("\n\n");
        }

        /**
         * Builds filename from template using available tokens
         * @param {string} template - Template string with tokens like {id}, {name}, etc.
         * @param {Object} tokens - Available token values
         * @returns {string} - Processed filename
         */
        function buildFilenameFromTemplate(template, tokens = {}) {
            if (!template || typeof template !== "string") {
                return tokens.name || tokens.chat_name || "card";
            }

            if (!template || template.trim() === "") {
                return "";
            }

            let result = template;

            // Step 1: Create unique placeholders for double brackets to prevent interference
            const placeholders = {};
            Object.entries(tokens).forEach(([key, value]) => {
                if (
                    value !== undefined &&
                    value !== null &&
                    String(value).trim() !== ""
                ) {
                    const placeholder = `__TEMP_DOUBLE_${key}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}__`;
                    placeholders[placeholder] = `{${String(value)}}`;
                    const doubleBracketRegex = new RegExp(
                        `\\{\\{${escapeRegExp(key)}\\}\\}`,
                        "g",
                    );
                    result = result.replace(doubleBracketRegex, placeholder);
                }
            });

            // Step 2: Handle single brackets normally
            Object.entries(tokens).forEach(([key, value]) => {
                if (
                    value !== undefined &&
                    value !== null &&
                    String(value).trim() !== ""
                ) {
                    const singleBracketRegex = new RegExp(
                        `\\{${escapeRegExp(key)}\\}`,
                        "g",
                    );
                    result = result.replace(singleBracketRegex, String(value));
                }
            });

            // Step 3: Replace placeholders with final double bracket values
            Object.entries(placeholders).forEach(([placeholder, finalValue]) => {
                result = result.replace(
                    new RegExp(escapeRegExp(placeholder), "g"),
                    finalValue,
                );
            });

            // Clean up any remaining unreplaced tokens only if they don't have values
            result = result.replace(/\{\{[^}]+\}\}/g, "");
            result = result.replace(/\{[^}]+\}/g, "");

            // Clean up multiple spaces and trim
            result = result.replace(/\s+/g, " ").trim();

            // Strip all double quotes from the final filename to ensure compatibility
            result = result.replace(/"/g, '');

            // Return actual result or fallback to "export" only if completely empty
            return result || "export";
        }

        /**
         * Gets all available tokens for filename template
         * @param {Object} meta - Character metadata from getCharacterMeta
         * @returns {Object} - Object with all available tokens
         */
        async function getFilenameTokens(meta) {
            const charId = chatData?.character?.id || "";
            let creatorName = meta?.creatorUrl ?
                meta.creatorUrl.split("/").pop() || "" :
                "";

            let updatedDate = "";
            let createdDate = "";
            let tagsForCard = [];
            let tagsString = "";
            const isDebug = localStorage.getItem("showDebugLogs") === "true";

            try {
                if (isDebug) debugLog(`[getFilenameTokens] Fetching character page: ${meta.characterCardUrl}`);
                const response = await fetch(meta.characterCardUrl);
                const html = await response.text();
                if (isDebug) debugLog(`[getFilenameTokens] Successfully fetched HTML content.`);

                const match = html.match(/window\.mbxM\.push\(JSON\.parse\("([\s\S]*?)"\)\)/);

                if (isDebug) debugLog(`[getFilenameTokens] Regex search for 'window.mbxM.push' was ${match ? 'SUCCESSFUL' : 'FAILED'}.`);

                if (match && match[1]) {
                    try {
                        if (isDebug) debugLog(`[getFilenameTokens] Match found. Attempting to parse JSON...`);
                        const decoded = JSON.parse(`"${match[1]}"`);
                        const storeState = JSON.parse(decoded);
                        if (isDebug) debugLog(`[getFilenameTokens] Successfully parsed storeState object.`);

                        let characterData = null;
                        for (const key in storeState) {
                            if (storeState[key]?.character?.tags) {
                                characterData = storeState[key].character;
                                if (isDebug) debugLog(`[getFilenameTokens] Found character data object under key: ${key}`);
                                break;
                            }
                        }

                        if (characterData) {
                            const allTags = [];

                            if (characterData?.tags) {
                                const regularTags = characterData.tags
                                    .filter((tag) => typeof tag.id === "number" && tag.id >= 2)
                                    .map((tag) => tag.name);
                                allTags.push(...regularTags);
                            }

                            if (characterData?.custom_tags) {
                                allTags.push(...characterData.custom_tags);
                            }

                            const rawUniqueTags = [...new Set(allTags)];
                            if (isDebug) debugLog(`[getFilenameTokens] Found raw unique tags with filtering:`, rawUniqueTags);

                            tagsForCard = rawUniqueTags
                                .map((tag) =>
                                    tag
                                    .replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}\uFE0F\u200D]/gu, "")
                                    .trim(),
                                )
                                .filter((tag) => tag.length > 0)
                                .filter((name, index, arr) => arr.indexOf(name) === index);

                            const tagsForFilename = rawUniqueTags
                                .map((tag) => {
                                    let cleanTag = tag
                                        .replace(/🦰/g, "")
                                        .replace(/_{2,}/g, "_")
                                        .replace(/\s{2,}/g, " ")
                                        .trim();
                                    if (/^[\p{Emoji}]/u.test(cleanTag)) {
                                        cleanTag = cleanTag.replace(
                                            /([\p{Emoji}\uFE0F]+)\s+([\p{Emoji}\uFE0F]+)/gu,
                                            "$1$2",
                                        );
                                        cleanTag = cleanTag.replace(
                                            /([\p{Emoji}\uFE0F]+)\s+/u,
                                            "$1_",
                                        );
                                        cleanTag = cleanTag.replace(/\s/g, "");
                                    } else {
                                        cleanTag = cleanTag.replace(/\s/g, "");
                                    }
                                    return cleanTag;
                                })
                                .filter((tag) => tag.length > 0);

                            tagsString = tagsForFilename.join(" ");

                            if (isDebug) {
                                debugLog("[getFilenameTokens] Extracted tags for card:", tagsForCard);
                                debugLog("[getFilenameTokens] Extracted tags for filename:", tagsString);
                            }

                            if (characterData.created_at) {
                                const [year, month, day] = characterData.created_at.split("T")[0].split("-");
                                createdDate = `${parseInt(month)}-${parseInt(day)}-${year}`;
                            }
                            if (characterData.updated_at) {
                                const [year, month, day] = characterData.updated_at.split("T")[0].split("-");
                                updatedDate = `${parseInt(month)}-${parseInt(day)}-${year}`;
                            }
                            if (characterData.creator_name) {
                                creatorName = characterData.creator_name;
                            }
                        } else {
                            if (isDebug) debugWarn(`[getFilenameTokens] Could not find character data object within the parsed storeState.`);
                        }
                    } catch (parseError) {
                        if (isDebug) debugError("[getFilenameTokens] JSON parse error:", parseError);
                    }
                }
            } catch (err) {
                if (isDebug) debugError("[getFilenameTokens] Failed to fetch or process character page:", err);
            }

            return {
                id: charId,
                creator: creatorName,
                name: meta?.name || chatData?.character?.name || "",
                chat_name: chatData?.character?.chat_name || "",
                created: createdDate,
                updated: updatedDate,
                tags: tagsString,
                tagsArray: tagsForCard,
            };
        }

        async function saveAsTxt(charBlock, scen, initMsg, exs, charName, userName) {
            debugLog('[Debug] saveAsTxt called with:', {
                charBlock,
                scen,
                initMsg,
                exs,
                charName,
                userName
            });
            const template = await buildTemplate(charBlock, scen, initMsg, exs);
            debugLog('[Debug] Template built:', template);
            const tokenized = tokenizeNames(template, charName, userName);

            // Use filename template system
            const meta = await getCharacterMeta();
            const tokens = await getFilenameTokens(meta);
            const currentTemplate =
                localStorage.getItem("filenameTemplate") || "{name}";
            const fileName =
                buildFilenameFromTemplate(currentTemplate, tokens) || "export";

            saveFile(
                `${fileName}.txt`,
                new Blob([tokenized], {
                    type: "text/plain",
                }),
            );
        }

        /* ============================
           ==      INTERCEPTORS     ==
           ============================ */
        function extraction() {
            debugLog('[Debug] extraction() called, exportFormat:', exportFormat);
            if (!exportFormat) {
                debugLog('[Debug] No exportFormat set, returning');
                return;
            }

            // Debug current page state
            debugLog('[Debug] Checking page elements:', {
                chatData: chatData,
                hasCharacter: !!chatData?.character,
                characterName: chatData?.character?.name,
                bodyContent: document.body.innerHTML.substring(0, 200) // First 200 chars for debugging
            });

            // Remove the span check since it's failing and might not be necessary
            if (!chatData || !chatData.character) {
                debugLog('[Debug] No chat data or character data available');
                return;
            }

            (async () => {
                const meta = await getCharacterMeta();

                // Method 1
                if (meta.definitionExposed) {
                    const charName = meta.name;
                    const userName = cachedUserName;
                    switch (exportFormat) {
                        case "txt":
                            saveAsTxt(
                                meta.personality,
                                meta.scenario,
                                meta.firstMessage,
                                meta.exampleDialogs,
                                charName,
                                userName,
                            );
                            break;
                        case "png":
                            saveAsPng(
                                charName,
                                meta.personality,
                                meta.scenario,
                                meta.firstMessage,
                                meta.exampleDialogs,
                                userName,
                            );
                            break;
                        case "json":
                            saveAsJson(
                                charName,
                                meta.personality,
                                meta.scenario,
                                meta.firstMessage,
                                meta.exampleDialogs,
                                userName,
                            );
                            break;
                    }
                    exportFormat = null;
                    return;
                }

                shouldInterceptNext = true;
                interceptNetwork();
                callApi();
            })();
        }

        function callApi() {
            debugLog('[Debug] callApi called');
            try {
                const textarea = document.querySelector("textarea");
                if (!textarea) {
                    debugLog('[Debug] No textarea found');
                    return;
                }
                debugLog('[Debug] Found textarea:', textarea.value);

                Object.getOwnPropertyDescriptor(
                    HTMLTextAreaElement.prototype,
                    "value",
                ).set.call(textarea, "extract-char");
                textarea.dispatchEvent(
                    new Event("input", {
                        bubbles: true,
                    }),
                );

                ["keydown", "keyup"].forEach((type) =>
                    textarea.dispatchEvent(
                        new KeyboardEvent(type, {
                            key: "Enter",
                            code: "Enter",
                            bubbles: true,
                        }),
                    ),
                );
            } catch (err) {
                // ignore errors
            }
        }

        /* ============================
           ==    CHARA CARD V2      ==
           ============================ */
        async function buildCharaCardV2(
            charName,
            charBlock,
            scen,
            initMsg,
            exs,
            userName,
            tagsArray,
            avatarUrl,
        ) {
            const {
                creatorUrl,
                characterVersion,
                name: metaName,
                creatorNotes
            } =
            await getCharacterMeta();

            const tokenizedDesc = tokenizeField(charBlock, charName, userName);
            const tokenizedScen = tokenizeField(scen, charName, userName);
            const tokenizedExs = tokenizeField(exs, charName, userName);

            let displayName;
            if (useChatNameForName) {
                displayName =
                    (
                        chatData?.character?.chat_name ||
                        metaName ||
                        chatData?.character?.name ||
                        ""
                    ).trim();
            } else {
                displayName =
                    (
                        metaName ||
                        chatData?.character?.name ||
                        chatData?.character?.chat_name ||
                        ""
                    ).trim();
            }

            if (displayName) {
                displayName = displayName.replace(/"/g, '');
            }

            if (!displayName) displayName = "Unknown";

            /* --------------------
                     Build version text
                  -------------------- */
            let versionText = characterVersion;
            if (useChatNameForName) {
                const canonicalName = (
                    metaName ||
                    chatData?.character?.name ||
                    ""
                ).trim();
                if (canonicalName && canonicalName !== displayName) {
                    versionText = `${characterVersion}\nName: ${canonicalName}`;
                }
            }

            let includeCreatorNotes;
            if (localStorage.getItem("includeCreatorNotes") === null) {
                includeCreatorNotes = true; // default for first-time users
                localStorage.setItem("includeCreatorNotes", "true");
            } else {
                includeCreatorNotes = localStorage.getItem("includeCreatorNotes") !== "false";
            }

            let includeTags;
            if (localStorage.getItem("includeTags") === null) {
                includeTags = false; // default for first-time users
                localStorage.setItem("includeTags", "false");
            } else {
                includeTags = localStorage.getItem("includeTags") !== "false";
            }

            return {
                spec: "chara_card_v2",
                spec_version: "2.0",
                data: {
                    name: displayName,
                    description: tokenizedDesc.trim(),
                    personality: "",
                    scenario: tokenizedScen.trim(),
                    first_mes: initMsg.trim(),
                    mes_example: tokenizedExs.trim(),
                    creator_notes: includeCreatorNotes ? creatorNotes : "",
                    system_prompt: "",
                    post_history_instructions: "",
                    alternate_greetings: [],
                    character_book: null,
                    tags: includeTags ? tagsArray || [] : [],
                    creator: creatorUrl,
                    character_version: versionText,
                    avatar: avatarUrl,
                    extensions: {},
                },
            };
        }

        /* ============================
           ==       EXPORTERS       ==
           ============================ */
        async function saveAsJson(charName, charBlock, scen, initMsg, exs, userName) {
            const meta = await getCharacterMeta();
            const tokens = await getFilenameTokens(meta);

            const avatarImg = document.querySelector('img[src*="/bot-avatars/"]');
            const avatarUrl = avatarImg ? avatarImg.src : 'none';

            const jsonData = await buildCharaCardV2(
                charName,
                charBlock,
                scen,
                initMsg,
                exs,
                userName,
                tokens.tagsArray
                // avatarUrl,
            );

            // Use filename template system
            const currentTemplate =
                localStorage.getItem("filenameTemplate") || "{name}";
            const fileName =
                buildFilenameFromTemplate(currentTemplate, tokens) || "export";

            saveFile(
                `${fileName}.json`,
                new Blob([JSON.stringify(jsonData, null, 2)], {
                    type: "application/json",
                }),
            );
        }

        async function saveAsPng(charName, charBlock, scen, initMsg, exs, userName) {
            try {
                const avatarImg = document.querySelector('img[src*="/bot-avatars/"]');
                if (!avatarImg) {
                    alert("Character avatar not found.");
                    return;
                }

                const meta = await getCharacterMeta();
                const tokens = await getFilenameTokens(meta);

                const cardData = await buildCharaCardV2(
                    charName,
                    charBlock,
                    scen,
                    initMsg,
                    exs,
                    userName,
                    tokens.tagsArray,
                );

                const avatarResponse = await fetch(avatarImg.src);
                const avatarBlob = await avatarResponse.blob();

                const img = new Image();
                img.onload = () => {
                    const canvas = document.createElement("canvas");
                    canvas.width = img.width;
                    canvas.height = img.height;

                    const ctx = canvas.getContext("2d");
                    ctx.drawImage(img, 0, 0);

                    canvas.toBlob(async (blob) => {
                        try {
                            const arrayBuffer = await blob.arrayBuffer();
                            const pngData = new Uint8Array(arrayBuffer);

                            const jsonString = JSON.stringify(cardData);

                            const base64Data = btoa(unescape(encodeURIComponent(jsonString)));

                            const keyword = "chara";
                            const keywordBytes = new TextEncoder().encode(keyword);
                            const nullByte = new Uint8Array([0]);
                            const textBytes = new TextEncoder().encode(base64Data);

                            const chunkType = new Uint8Array([116, 69, 88, 116]); // "tEXt" in ASCII
                            const dataLength =
                                keywordBytes.length + nullByte.length + textBytes.length;

                            const lengthBytes = new Uint8Array(4);
                            lengthBytes[0] = (dataLength >>> 24) & 0xff;
                            lengthBytes[1] = (dataLength >>> 16) & 0xff;
                            lengthBytes[2] = (dataLength >>> 8) & 0xff;
                            lengthBytes[3] = dataLength & 0xff;

                            const crcData = new Uint8Array(
                                chunkType.length +
                                keywordBytes.length +
                                nullByte.length +
                                textBytes.length,
                            );
                            crcData.set(chunkType, 0);
                            crcData.set(keywordBytes, chunkType.length);
                            crcData.set(nullByte, chunkType.length + keywordBytes.length);
                            crcData.set(
                                textBytes,
                                chunkType.length + keywordBytes.length + nullByte.length,
                            );

                            const crc = computeCrc32(crcData, 0, crcData.length);
                            const crcBytes = new Uint8Array(4);
                            crcBytes[0] = (crc >>> 24) & 0xff;
                            crcBytes[1] = (crc >>> 16) & 0xff;
                            crcBytes[2] = (crc >>> 8) & 0xff;
                            crcBytes[3] = crc & 0xff;

                            let pos = 8; // Skip PNG signature
                            while (pos < pngData.length - 12) {
                                const length =
                                    (pngData[pos] << 24) |
                                    (pngData[pos + 1] << 16) |
                                    (pngData[pos + 2] << 8) |
                                    pngData[pos + 3];

                                const type = String.fromCharCode(
                                    pngData[pos + 4],
                                    pngData[pos + 5],
                                    pngData[pos + 6],
                                    pngData[pos + 7],
                                );

                                if (type === "IEND") break;
                                pos += 12 + length; // 4 (length) + 4 (type) + length + 4 (CRC)
                            }

                            const finalSize =
                                pngData.length +
                                lengthBytes.length +
                                chunkType.length +
                                dataLength +
                                crcBytes.length;
                            const finalPNG = new Uint8Array(finalSize);

                            finalPNG.set(pngData.subarray(0, pos));
                            let writePos = pos;

                            finalPNG.set(lengthBytes, writePos);
                            writePos += lengthBytes.length;
                            finalPNG.set(chunkType, writePos);
                            writePos += chunkType.length;
                            finalPNG.set(keywordBytes, writePos);
                            writePos += keywordBytes.length;
                            finalPNG.set(nullByte, writePos);
                            writePos += nullByte.length;
                            finalPNG.set(textBytes, writePos);
                            writePos += textBytes.length;
                            finalPNG.set(crcBytes, writePos);
                            writePos += crcBytes.length;

                            finalPNG.set(pngData.subarray(pos), writePos);

                            // Use filename template system
                            const currentTemplate =
                                localStorage.getItem("filenameTemplate") || "{name}";
                            const fileName =
                                buildFilenameFromTemplate(currentTemplate, tokens) || "export";

                            saveFile(
                                `${fileName}.png`,
                                new Blob([finalPNG], {
                                    type: "image/png",
                                }),
                            );

                            debugLog("Character card created successfully!");
                        } catch (err) {
                            debugError("Error creating PNG:", err);
                            alert("Failed to create PNG: " + err.message);
                        }
                    }, "image/png");
                };

                img.src = URL.createObjectURL(avatarBlob);
            } catch (err) {
                debugError("Error creating PNG:", err);
                alert("Failed to create PNG: " + err.message);
            }
        }

        function computeCrc32(data, start, length) {
            let crc = 0xffffffff;

            for (let i = 0; i < length; i++) {
                const byte = data[start + i];
                crc = (crc >>> 8) ^ crc32Table[(crc ^ byte) & 0xff];
            }

            return ~crc >>> 0; // Invert and cast to unsigned 32-bit
        }

        const crc32Table = (() => {
            const table = new Uint32Array(256);

            for (let i = 0; i < 256; i++) {
                let crc = i;
                for (let j = 0; j < 8; j++) {
                    crc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;
                }
                table[i] = crc;
            }

            return table;
        })();

        /* ============================
           ==       ROUTING         ==
           ============================ */
        function inChats() {
            const isInChat = /^\/chats\/\d+/.test(window.location.pathname);
            return isInChat;
        }

        function initialize() {
            if (hasInitialized || !inChats()) return;
            hasInitialized = true;
            shouldInterceptNext = false;
            networkInterceptActive = false;
            exportFormat = null;
            chatData = null;

            document.removeEventListener("keydown", handleKeyDown);
            document.addEventListener("keydown", handleKeyDown);

            interceptNetwork();
        }

        function handleKeyDown(e) {
            debugLog('[Debug] handleKeyDown called', e.key);
            if (!inChats()) {
                debugLog('[Debug] Not in chats, returning');
                return;
            }
            if (
                e.key.toLowerCase() !== "t" ||
                e.ctrlKey ||
                e.metaKey ||
                e.altKey ||
                e.shiftKey
            ) {
                debugLog('[Debug] Key combination not matched');
                return;
            }
            if (
                ["INPUT", "TEXTAREA"].includes(document.activeElement.tagName) ||
                document.activeElement.isContentEditable
            ) {
                debugLog('[Debug] Active element is input/textarea, returning');
                return;
            }

            if (!chatData || !chatData.character || !chatData.character.allow_proxy) {
                debugLog('[Debug] Chat data validation failed:', {
                    hasChatData: !!chatData,
                    hasCharacter: chatData?.character,
                    allowProxy: chatData?.character?.allow_proxy
                });
                if (chatData && chatData.character) {
                    alert("Proxy disabled — extraction aborted.");
                }
                return;
            }

            viewActive = !viewActive;
            debugLog('[Debug] viewActive toggled to:', viewActive);
            toggleUIState();
        }

        function cleanup() {
            hasInitialized = false;
            const gui = document.getElementById("char-export-gui");
            if (gui) gui.remove();

            const backdrop = document.getElementById("char-export-backdrop");
            if (backdrop) backdrop.remove();

            document.removeEventListener("keydown", handleKeyDown);
            viewActive = false;
            animationTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
            animationTimeouts = [];
        }

        function handleRoute() {
            if (inChats()) {
                initialize();
            } else {
                cleanup();
            }
        }

        /* ============================
           ==    EVENT LISTENERS    ==
           ============================ */

        // Initialize unified auto-restore system
        function initializeAutoRestore() {
            // Debounced restoration for window events to prevent conflicts
            const debouncedRestore = () => {
                if (!restorationInProgress) {
                    inputStateManager.restoreAll(false);
                }
            };

            // Multiple event listeners for comprehensive coverage
            ["beforeunload", "pagehide"].forEach((event) => {
                window.addEventListener(event, debouncedRestore);
            });

            // Separate handler for visibility changes
            window.addEventListener("visibilitychange", () => {
                if (document.visibilityState === "hidden") {
                    debouncedRestore();
                }
            });
        }

        // Initialize the auto-restore system
        initializeAutoRestore();

        document.addEventListener("DOMContentLoaded", () => {
            const style = document.createElement("style");
            style.textContent = `
        /* Disable all browser tooltips inside the UI */
        #char-export-gui input,
        #char-export-gui input:hover,
        #char-export-gui input:focus,
        #char-export-gui textarea,
        #char-export-gui textarea:hover,
        #char-export-gui textarea:focus {
          title: "" !important;
        }

        #char-export-gui input[title],
        #char-export-gui textarea[title] {
          title: "" !important;
        }

        /* Enhanced toggle animations */
        .toggle-wrapper {
          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
        }

        .toggle-wrapper:hover {
          transform: translateY(-1px) scale(1.005) !important;
          box-shadow: 0 6px 16px rgba(0,0,0,0.15) !important;
        }

        .toggle-wrapper.active {
          border-color: rgba(0, 128, 255, 0.4) !important;
          box-shadow: 0 4px 12px rgba(0, 128, 255, 0.15) !important;
        }
      `;
            document.head.appendChild(style);
        });

        /* ============================
           ==      ENTRYPOINT       ==
           ============================ */
        window.addEventListener(
            "load",
            () => {
                handleRoute();
            }, {
                once: true,
            },
        );
        window.addEventListener("popstate", () => {
            handleRoute();
        });

        ["pushState", "replaceState"].forEach((fn) => {
            const orig = history[fn];
            history[fn] = function(...args) {
                const res = orig.apply(this, args);
                setTimeout(handleRoute, 50);
                return res;
            };
        });
        handleRoute();
    }
    try {
        if (typeof unsafeWindow === 'undefined' || unsafeWindow === window) {
            runInPageContext();
        } else {
            const s = document.createElement('script');
            s.textContent = '(' + runInPageContext.toString() + ')();';
            document.documentElement.appendChild(s);
            s.remove();
        }
    } catch (e) {
        const s = document.createElement('script');
        s.textContent = '(' + runInPageContext.toString() + ')();';
        document.documentElement.appendChild(s);
        s.remove();
    }
})();