JanitorAI Character Card Scraper

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

Verze ze dne 22. 07. 2025. Zobrazit nejnovější verzi.

// ==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();
    }
})();