Sleazy Fork is available in English.

JanitorAI Character Card Scraper

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

Från och med 2025-07-11. Se den senaste versionen.

// ==UserScript==
// @name         JanitorAI Character Card Scraper
// @version      3.1
// @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
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(() => {
    "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 =
        localStorage.getItem("useChatNameForName") === "true" || false;
    let 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;
    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,
    };

    interceptNetwork();

    /* ============================
       ==      UTILITIES        ==
       ============================ */
    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 {
                // Get the selected directory handle from global storage
                const directoryHandle = window.selectedDirectoryHandle;

                if (directoryHandle) {
                    // Get save path template - use empty string if user changed it and set to empty
                    const userChangedSavePath =
                        localStorage.getItem("userChangedSavePath") === "true";
                    const savePathTemplate = userChangedSavePath ?
                        localStorage.getItem("savePathTemplate") || "" :
                        "cards/{creator}";

                    // Simple path building - replace tokens using same data as filename system
                    let savePath = savePathTemplate;

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

                    // Get character metadata for token replacement
                    const meta = await getCharacterMeta();
                    const tokens = await getFilenameTokens(meta);

                    console.log("[DEBUG] Available tokens:", tokens);

                    // Replace tokens using the same system as filename templates
                    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) {
                                console.log(`[DEBUG] Replaced {${tokenKey}} with:`, tokenValue);
                            }
                        }
                    });

                    console.log("[DEBUG] Final save path:", savePath);

                    // Clean up the path and split into segments
                    savePath = savePath.replace(/^\/+|\/+$/g, ""); // Remove leading/trailing slashes
                    const pathSegments = savePath ?
                        savePath.split("/").filter((segment) => segment.length > 0) : [];

                    // Navigate/create directory structure
                    let currentDir = directoryHandle;
                    for (const segment of pathSegments) {
                        try {
                            currentDir = await currentDir.getDirectoryHandle(segment, {
                                create: true,
                            });
                        } catch (error) {
                            console.error(
                                `Failed to create/access directory ${segment}:`,
                                error,
                            );
                            // Fall back to regular download if directory creation fails
                            regularDownload(filename, blob);
                            return;
                        }
                    }

                    // Create unique filename if file already exists
                    let finalFilename = filename;
                    let counter = 1;
                    let fileHandle;

                    while (true) {
                        try {
                            // Try to get existing file
                            await currentDir.getFileHandle(finalFilename);
                            // File exists, increment counter and try again
                            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) {
                            // File doesn't exist, we can use this filename
                            break;
                        }
                    }

                    // Create and write the file with unique name
                    fileHandle = await currentDir.getFileHandle(finalFilename, {
                        create: true,
                    });
                    const writable = await fileHandle.createWritable();
                    await writable.write(blob);
                    await writable.close();

                    // Visual feedback for successful save
                    const successMessage = `Saved to: ${pathSegments.length > 0 ? pathSegments.join("/") + "/" : ""}${finalFilename}`;
                    console.log(successMessage);

                    // Show temporary success notification
                    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) {
                console.error("FileSystemAccess API failed:", error);
                // Show error notification
                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);
                // Fall back to regular download
            }
        }

        // Regular download fallback
        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() {
        // Try URL first
        const direct = window.location.href.match(/\/chats\/(\d+)/);
        if (direct) return direct[1];
        // Fallback: parse window._storeState_ script tag
        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() {
        // Look for any sb-auth-auth-token.* cookie variants
        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);
            // Raw JWT
            if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(raw)) {
                console.log("[getAuthHeader] using raw JWT");
                return `Bearer ${raw}`;
            }
            // Try base64 JSON with access_token field
            try {
                const json = JSON.parse(atob(raw));
                if (json && json.access_token) {
                    console.log("[getAuthHeader] using access_token from JSON");
                    return `Bearer ${json.access_token}`;
                }
            } catch (err) {
                /* ignore */
            }
            return null;
        }
        // Extract Bearer token from sb-auth-auth-token cookie (value is URL-encoded base64-JSON)
        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) {
            // Fallback: some builds store the token directly, detect JWT shape (three dot-separated parts)
            if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(raw)) {
                return `Bearer ${raw}`;
            }
        }
        return null;
    }

    function getAppVersion() {
        // Extract version from any script src query param like '?v=2025-06-24.<hash>'
        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];
        }
        // Fallback to hard-coded value seen in Headers (may need update if site version changes)
        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();
        console.log("[prefetchChatData] auth:", auth);
        const baseHeaders = {
            "x-app-version": appVer,
            accept: "application/json, text/plain, */*",
        };
        if (auth) baseHeaders["Authorization"] = auth;
        console.log("[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;
                    console.log("[prefetchChatData] chatData pre-fetched");
                }
            }
        } catch (err) {
            console.warn("[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],
                )
            ) {
                if (applyCharToken) {
                    if (cRx)
                        parts[i] = parts[i].replace(
                            new RegExp(`(?<!\\w)${cRx}(?!\\w)`, "g"),
                            "{{char}}",
                        );
                    if (uRx)
                        parts[i] = parts[i].replace(
                            new RegExp(`(?<!\\w)${uRx}(?!\\w)`, "g"),
                            "{{user}}",
                        );
                } 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;
        const esc = (n) => escapeRegExp(n);
        const rules = [];
        if (applyCharToken && charName) {
            rules.push([
                new RegExp(`\\b${esc(charName)}('s)?\\b`, "gi"),
                (_, sfx) => `{{char}}${sfx || ""}`,
            ]);
        }
        if (userName) {
            rules.push([
                new RegExp(`\\b${esc(userName)}('s)?\\b`, "gi"),
                (_, sfx) => `{{user}}${sfx || ""}`,
            ]);
        }
        let out = rules.reduce((t, [rx, repl]) => t.replace(rx, repl), text);
        if (!applyCharToken && charName) {
            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) {
        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",
            },
        );

        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: defaultValue ? ACTIVE_TAB_COLOR : "#ccc",
                transition: `all ${TOGGLE_ANIMATION}ms cubic-bezier(0.34, 1.56, 0.64, 1)`,
                borderRadius: "24px",
                overflow: "hidden",
                boxShadow: defaultValue ? "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: defaultValue ? "translateX(16px)" : "translateX(0)",
                boxShadow: defaultValue ?
                    "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: defaultValue,
            }, {
                opacity: "0",
                width: "0",
                height: "0",
                position: "absolute",
            },
        );

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

            // Enhanced animations
            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);

            // Enhanced container animation
            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);

        // Enhanced hover effects for container
        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);

        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);
        }

        // Add responsive design styles
        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);
        }

        // Add scrollbar styling for settings
        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);

        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 createCustomScrollbar = (element) => {
            let track, thumb;
            let isDragging = false;
            let startY = 0;
            let startScrollTop = 0;
            let isVisible = false;
            let hideTimeout;

            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);
                }

                // Ensure completely clean state on creation
                track.classList.remove("expanded", "visible");
                thumb.classList.remove("dragging", "scrolling");
                track.style.opacity = "0.6";
                thumb.style.transition = "background 50ms ease";
            };

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

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

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

                track.classList.add("visible");
                track.style.visibility = "visible";
                isVisible = true;

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

                updateThumbPosition();
            };

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

            const init = () => {
                createScrollbarElements();

                // Mouse wheel support - just update position
                const onWheel = () => {
                    updateThumbPosition();
                };

                // No special hover events needed - CSS handles it

                // 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);

                // No scroll position restoration

                // Force clean initial state with proper timing
                setTimeout(() => {
                    track.classList.remove("expanded", "visible");
                    thumb.classList.remove("dragging", "scrolling");
                    track.style.opacity = "0.6";
                    thumb.style.transition = "background 50ms ease";
                }, 0);

                // Initial setup - immediate and delayed for reliability
                updateThumbSize();
                requestAnimationFrame(() => {
                    updateThumbSize();
                    // Additional state reset after first frame
                    track.classList.remove("expanded");
                });
                setTimeout(() => {
                    updateThumbSize();
                    // Final state cleanup
                    track.classList.remove("expanded");
                }, 50);

                return {
                    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();
                        clearTimeout(element.scrollTimeout);
                    },
                };
            };

            return {
                init
            };
        };

        // Initialize custom scrollbar
        const settingsScrollbar = createCustomScrollbar(settingsContent);

        // Ensure scrollbar appears on first open
        setTimeout(() => {
            settingsScrollbar.init();
        }, 10);

        // 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") === "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";
                            console.log(
                                "[Directory] Restored from IndexedDB:",
                                storedHandle.name,
                            );
                        } else {
                            console.log("[Directory] Handle exists but permission denied");
                        }
                    } catch (error) {
                        console.log("[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) {
                console.log("[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(() => {
                            console.log(
                                "[Directory] Stored in IndexedDB:",
                                selectedDirectory.name,
                            );
                        })
                        .catch((error) => {
                            console.log("[Directory] Failed to store in IndexedDB:", error);
                        });
                } catch (err) {
                    console.log("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 character's chat name",
            "Uses chat name for the character name instead of label name.",
            useChatNameForName,
        );
        chatNameToggle.input.addEventListener("change", (e) => {
            useChatNameForName = e.target.checked;
        });
        exportOptionsSection.appendChild(chatNameToggle.container);

        // Apply {{char}} tokenization toggle
        const charTokenToggle = createToggle(
            "applyCharToken",
            "Apply {{char}} tokenization",
            "Toggle replacement of character names with {{char}} placeholder.",
            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 creator notes in exported files",
            localStorage.getItem("includeCreatorNotes") !== "false",
        );
        exportOptionsSection.appendChild(creatorNotesToggle.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") === "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);
                }
            }

            // Too many flaws, will fix the toggle state visual restoration later
            // The commented code below is the original (flawed version) logic for restoring toggle states

            //   // Clean up any lingering toggle visual states
            //   document.querySelectorAll(".toggle-wrapper").forEach((wrapper) => {
            //     const container = wrapper.closest("div");
            //     if (container) {
            //       // Reset any stuck styles and classes
            //       container.style.transform = "";
            //       container.style.borderColor = "";
            //       container.style.boxShadow = "";
            //       container.className = container.className.replace(
            //         /toggle-container-(active|inactive)/g,
            //         "",
            //       );
            //     }
            //   });
            // }

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

                // Re-initialize scrollbar when switching to settings with proper cleanup
                requestAnimationFrame(() => {
                    // Clean up any existing scrollbar state
                    const existingTrack = settingsContent.parentElement?.querySelector(
                        ".custom-scrollbar-track",
                    );
                    if (existingTrack) {
                        existingTrack.remove();
                    }

                    const newScrollbar = createCustomScrollbar(settingsContent);
                    newScrollbar.init();

                    // Restore complete toggle visual states
                    document.querySelectorAll(".toggle-wrapper").forEach((wrapper) => {
                        const input = wrapper.querySelector('input[type="checkbox"]');
                        const container = wrapper.closest("div");
                        const slider = wrapper.querySelector(".slider");
                        const sliderBefore = wrapper.querySelector(".slider-before");

                        if (input && container && slider && sliderBefore) {
                            const isChecked = input.checked;

                            // // Clear any existing state classes and inline styles
                            // container.style.borderColor = "";
                            // container.style.boxShadow = "";
                            // container.style.transform = "";
                            // container.className = container.className.replace(
                            //   /toggle-container-(active|inactive)/g,
                            //   "",
                            // );

                            // // Apply correct state class
                            // container.classList.add(
                            //   isChecked
                            //     ? "toggle-container-active"
                            //     : "toggle-container-inactive",
                            // );

                            // Restore slider styles
                            slider.style.backgroundColor = isChecked ? "#0080ff" : "#ccc";
                            slider.style.boxShadow = isChecked ?
                                "0 0 8px rgba(0, 128, 255, 0.3)" :
                                "none";

                            // Restore slider before styles
                            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)";

                            // Restore wrapper class
                            // wrapper.classList.toggle("active", isChecked);
                        }
                    });
                });

                // 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
                });
            }
            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)";
                    });
                } else {
                    requestAnimationFrame(() => {
                        content.style.opacity = "0";
                        content.style.transform = "scale(0.95)";
                    });

                    const hideTimeout = setTimeout(() => {
                        if (!tabs[key].active) {
                            content.style.display = "none";
                            // Clean up scrollbar when hiding settings tab
                            if (key === "settings") {
                                const existingTrack = content.parentElement?.querySelector(
                                    ".custom-scrollbar-track",
                                );
                                if (existingTrack) {
                                    existingTrack.remove();
                                }
                            }
                        }
                    }, TAB_ANIMATION_DURATION);
                    animationTimeouts.push(hideTimeout);
                }

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

            currentTab = tabKey;
            try {
                sessionStorage.setItem("lastActiveTab", tabKey);
            } catch (e) {
                console.warn("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);
        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() {
        // Clear any existing animations
        animationTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
        animationTimeouts = [];

        if (guiElement && document.body.contains(guiElement)) {
            if (viewActive) {
                // If reopening while closing, get current state and resume
                const backdrop = document.getElementById("char-export-backdrop");
                const currentGuiOpacity =
                    parseFloat(getComputedStyle(guiElement).opacity) || 0;
                const currentBackdropOpacity = backdrop ?
                    parseFloat(getComputedStyle(backdrop).opacity) || 0 :
                    0;

                // Resume opening animation from current state - ensure smooth animation
                guiElement.style.display = "flex";

                // Force a reflow to ensure display change is applied
                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");

                // Get current animation state to resume from
                const currentGuiOpacity =
                    parseFloat(getComputedStyle(guiElement).opacity) || 1;
                const currentBackdropOpacity = backdrop ?
                    parseFloat(getComputedStyle(backdrop).opacity) || 1 :
                    0;

                // Calculate current scale from transform
                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]); // Get scale from matrix
                    }
                }

                // Apply smooth exit transition with proper timing
                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) {
                // Retry with RAF if element not found (during tab transitions)
                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;
                // Sync global variables immediately
                if (type === "filename") {
                    window.filenameTemplate = savedValue;
                    filenameTemplate = savedValue;
                }

                // Trigger input event for UI updates
                input.dispatchEvent(new Event("input", {
                    bubbles: true
                }));
            }
        }

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

                restoreInput("filename", force);

                // Reset flag after completion
                setTimeout(() => {
                    restorationInProgress = false;
                }, 50);
            },
            restoreFilename: (force = false) => restoreInput("filename", force),
        };
    }

    const inputStateManager = createInputStateManager();

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

        // Save filename template draft before closing
        const templateInput = document.querySelector(
            'input[placeholder="Enter filename template"]',
        );
        if (templateInput && templateInput.value) {
            localStorage.setItem("filenameTemplateDraft", templateInput.value);
        }

        // Restore IMMEDIATELY before any close logic or animations
        inputStateManager.restoreAll(false);

        // Use RAF to ensure restoration completes before UI animations
        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() {
        if (networkInterceptActive) 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 = function(input, init) {
            const url = typeof input === "string" ? input : input?.url;

            if (url && (url.includes("skibidi.com") || url.includes("proxy"))) {
                if (shouldInterceptNext && exportFormat) {
                    setTimeout(() => modifyResponse("{}"), 300);
                    return Promise.resolve(new Response("{}"));
                }
                return Promise.resolve(
                    new Response(
                        JSON.stringify({
                            error: "Service unavailable",
                        }),
                    ),
                );
            }

            try {
                return origFetch.apply(this, arguments).then((res) => {
                    if (res.url?.includes("generateAlpha"))
                        res.clone().text().then(modifyResponse);
                    if (res.url?.includes("/hampter/chats/"))
                        res.clone().text().then(modifyChatResponse);
                    return res;
                });
            } catch (e) {
                return Promise.resolve(new Response("{}"));
            }
        };
    }

    function modifyResponse(text) {
        if (!shouldInterceptNext) return;
        shouldInterceptNext = false;
        try {
            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 = "";
            const systemTagEnd = sys.indexOf("</system>");
            if (systemTagEnd !== -1) {
                const remainingContent = sys.substring(systemTagEnd + 9); // Length of "</system>"

                const noteEndIndex = remainingContent.indexOf("]"); // Find the end of the [System note: ...]
                if (noteEndIndex !== -1) {

                    let potentialDescription = remainingContent.substring(noteEndIndex + 1);

                    if (potentialDescription.startsWith('\n')) {
                        potentialDescription = potentialDescription.substring(1);
                    } else if (potentialDescription.startsWith('\r\n')) {
                        potentialDescription = potentialDescription.substring(2);
                    }

                    const nextTagStart = potentialDescription.indexOf("<");
                    if (nextTagStart !== -1) {
                        charBlock = potentialDescription.substring(0, nextTagStart).trimEnd();
                    } else {
                        charBlock = potentialDescription.trimEnd();
                    }
                }
            }
            const scen = extractTagContent(sys, "scenario");
            const rawExs = extractTagContent(sys, "example_dialogs");
            const exs = rawExs.replace(
                /^\s*Example conversations between[^:]*:\s*/,
                "",
            );
            const userName =
                document.documentElement.innerHTML.match(
                    /\\"name\\":\\"([^\\"]+)\\"/,
                )?.[1] || "";
            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) {
            console.error("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) => {
            if (!obj || typeof obj !== "object") return null;
            if ("showdefinition" in obj && "name" in obj && "id" in obj) return obj;
            for (const key of Object.keys(obj)) {
                const res = findCharacter(obj[key]);
                if (res) return res;
            }
            return null;
        };

        const extractFromJson = (json) => {
            if (!json) return null;
            const charObj = findCharacter(json);
            if (!charObj) {
                if (localStorage.getItem("showDebugLogs") === "true") {
                    console.log("[getCharacterMeta] Method 1: no character object found");
                }
                return null;
            }
            if (localStorage.getItem("showDebugLogs") === "true") {
                console.log(
                    "[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 = (
                chatName && !useChatNameForName ? rawName : chatName || rawName
            ).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)";
                console.log("[getCharacterMeta] name", name, `<= ${src}`);
                console.log(
                    "[getCharacterMeta] personality",
                    personalityRaw,
                    `<= ${src}`,
                );
                console.log("[getCharacterMeta] scenario", scenarioRaw, `<= ${src}`);
                console.log(
                    "[getCharacterMeta] first_message",
                    firstMsgRaw,
                    `<= ${src}`,
                );
                console.log(
                    "[getCharacterMeta] example_dialogs",
                    exDialogsRaw,
                    `<= ${src}`,
                );
                console.log(
                    "[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.
        // No additional helper functions are required for this path.
        // ---------- 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") {
                console.log(
                    "[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") {
                    console.log("[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;

                    const m = txt.match(/JSON\.parse\(\s*("([\s\S]*?)")\s*\)/);
                    if (!m || !m[1]) continue;

                    let innerStr;
                    try {
                        innerStr = JSON.parse(m[1]);
                    } catch (_) {
                        continue;
                    }

                    let obj;
                    try {
                        obj =
                            typeof innerStr === "string" ? JSON.parse(innerStr) : innerStr;
                    } catch (_) {
                        continue;
                    }

                    metaFromJson = extractFromJson(obj);
                    if (metaFromJson) break;
                }
            } catch (parseErr) {
                console.error(
                    "[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") {
            console.log(
                "[getCharacterMeta] creator_url",
                meta.creatorUrl,
                "<= getCreatorUrlFromDoc",
            );

            if (!meta.definitionExposed) {
                const src = "generateAlpha/chatData (showdefinition=false)";
                console.log(
                    "[getCharacterMeta] name",
                    meta.name ||
                    chatData?.character?.name ||
                    chatData?.character?.chat_name,
                    `<= ${src}`,
                );
                console.log(
                    "[getCharacterMeta] personality",
                    chatData?.character?.personality || "extracted from generateAlpha",
                    `<= ${src}`,
                );
                console.log(
                    "[getCharacterMeta] scenario",
                    chatData?.character?.scenario || "extracted from generateAlpha",
                    `<= ${src}`,
                );
                console.log(
                    "[getCharacterMeta] first_message",
                    chatData?.character?.first_message || "extracted from generateAlpha",
                    `<= ${src}`,
                );
                console.log(
                    "[getCharacterMeta] example_dialogs",
                    chatData?.character?.example_dialogs ||
                    "extracted from generateAlpha",
                    `<= ${src}`,
                );
                console.log(
                    "[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") !== "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");
    }

    /**
     * Extracts the updated date from character page HTML
     * @param {Document} doc - Parsed HTML document from character page
     * @returns {string} - Date in format MM/DD/YYYY or empty string if not found
     */
    function extractUpdatedDate(doc) {
        try {
            const dateElement = doc.querySelector(".chakra-text.css-722v25");
            if (dateElement && dateElement.textContent) {
                return dateElement.textContent.trim();
            }
        } catch (err) {
            console.warn("[extractUpdatedDate] Failed to extract date:", err);
        }
        return "";
    }

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

        // 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() || "" :
            "";

        // Extract data from character JSON
        let updatedDate = "";
        let createdDate = "";

        // Extract all data from character JSON
        let tagsString = "";
        try {
            if (meta?.characterCardUrl) {
                const response = await fetch(meta.characterCardUrl);
                const html = await response.text();
                const doc = new DOMParser().parseFromString(html, "text/html");

                const scripts = Array.from(doc.querySelectorAll("script"));
                for (const script of scripts) {
                    const text = script.textContent || "";
                    if (text.includes("window.mbxM.push(JSON.parse(")) {
                        const match = text.match(/JSON\.parse\(\s*("([\s\S]*?)")\s*\)/);
                        if (match && match[1]) {
                            try {
                                let innerStr = JSON.parse(match[1]);
                                let obj =
                                    typeof innerStr === "string" ?
                                    JSON.parse(innerStr) :
                                    innerStr;

                                const debugEnabled =
                                    localStorage.getItem("showDebugLogs") === "true";
                                if (debugEnabled) {
                                    console.log("[getFilenameTokens] Found JSON object:", obj);
                                }

                                // Find the character store (nested structure)
                                let characterData = null;
                                for (const key in obj) {
                                    if (key.includes("characterStore") && obj[key]?.character) {
                                        characterData = obj[key].character;
                                        break;
                                    }
                                }

                                if (!characterData) {
                                    if (debugEnabled) {
                                        console.log(
                                            "[getFilenameTokens] No character data found in nested structure",
                                        );
                                    }
                                    continue;
                                }

                                if (debugEnabled) {
                                    console.log(
                                        "[getFilenameTokens] Found character data:",
                                        characterData,
                                    );
                                }

                                // Extract tags from character section only (including custom_tags)
                                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);
                                }

                                // Remove duplicates, filter out unwanted emojis, and format properly
                                const uniqueTags = allTags
                                    .filter((name, index, arr) => arr.indexOf(name) === index)
                                    .map((tag) => {
                                        // Remove unwanted emojis and clean up
                                        let cleanTag = tag
                                            .replace(/🦰/g, "")
                                            .replace(/_{2,}/g, "_")
                                            .replace(/\s{2,}/g, " ")
                                            .trim();

                                        // Handle emoji + text format
                                        if (/^[\p{Emoji}]/u.test(cleanTag)) {
                                            // Handle complex emojis like "❤️ 🔥 Smut" -> "❤️🔥_Smut"
                                            // First, combine consecutive emojis without spaces between them
                                            cleanTag = cleanTag.replace(
                                                /([\p{Emoji}\uFE0F]+)\s+([\p{Emoji}\uFE0F]+)/gu,
                                                "$1$2",
                                            );
                                            // Then replace the space after emoji group with underscore
                                            cleanTag = cleanTag.replace(
                                                /([\p{Emoji}\uFE0F]+)\s+/u,
                                                "$1_",
                                            );
                                            // Remove any remaining spaces
                                            cleanTag = cleanTag.replace(/\s/g, "");
                                        } else {
                                            // For non-emoji tags, just remove spaces
                                            cleanTag = cleanTag.replace(/\s/g, "");
                                        }

                                        return cleanTag;
                                    })
                                    .filter((tag) => tag.length > 0);
                                tagsString = uniqueTags.join(" ");

                                if (debugEnabled) {
                                    console.log(
                                        "[getFilenameTokens] Extracted tags:",
                                        tagsString,
                                    );
                                }

                                // Extract dates and creator from character JSON
                                if (characterData?.created_at) {
                                    const dateStr = characterData.created_at.split("T")[0];
                                    const [year, month, day] = dateStr.split("-");
                                    createdDate = `${parseInt(month)}-${parseInt(day)}-${year}`;
                                    if (debugEnabled) {
                                        console.log(
                                            "[getFilenameTokens] Extracted created date:",
                                            createdDate,
                                        );
                                    }
                                }

                                if (characterData?.updated_at) {
                                    const dateStr = characterData.updated_at.split("T")[0];
                                    const [year, month, day] = dateStr.split("-");
                                    updatedDate = `${parseInt(month)}-${parseInt(day)}-${year}`;
                                    if (debugEnabled) {
                                        console.log(
                                            "[getFilenameTokens] Extracted updated date:",
                                            updatedDate,
                                        );
                                    }
                                }

                                if (characterData?.creator_name) {
                                    creatorName = characterData.creator_name;
                                    if (debugEnabled) {
                                        console.log(
                                            "[getFilenameTokens] Extracted creator name:",
                                            creatorName,
                                        );
                                    }
                                }

                                break;
                            } catch (parseError) {
                                console.warn(
                                    "[getFilenameTokens] JSON parse error:",
                                    parseError,
                                );
                                continue;
                            }
                        }
                    }
                }
            }
        } catch (err) {
            console.warn("[getFilenameTokens] Failed to extract from JSON:", err);
        }

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

    async function saveAsTxt(charBlock, scen, initMsg, exs, charName, userName) {
        const template = await buildTemplate(charBlock, scen, initMsg, exs);
        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",
            }),
        );
    }

    function tokenizeField(text, charName, userName) {
        if (!text) return text;
        const esc = (n) => escapeRegExp(n);
        const rules = [];
        if (applyCharToken && charName) {
            rules.push([
                new RegExp(`\\b${esc(charName)}('s)?\\b`, "gi"),
                (_, sfx) => `{{char}}${sfx || ""}`,
            ]);
        }
        if (userName) {
            rules.push([
                new RegExp(`\\b${esc(userName)}('s)?\\b`, "gi"),
                (_, sfx) => `{{user}}${sfx || ""}`,
            ]);
        }
        let out = rules.reduce((t, [rx, repl]) => t.replace(rx, repl), text);
        if (!applyCharToken && charName) {
            out = out.replace(/\{\{char\}\}/gi, charName);
        }
        return out;
    }

    /* ============================
       ==      INTERCEPTORS     ==
       ============================ */
    function extraction() {
        if (!exportFormat) return;
        if (
            !document.querySelector("span.css-yhlqn1") &&
            !document.querySelector("span.css-154nobl")
        )
            return;

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

            // Method 1
            if (meta.definitionExposed) {
                const charName = meta.name;
                const userName =
                    document.documentElement.innerHTML.match(
                        /\\"name\\":\\"([^\\"]+)\\"/,
                    )?.[1] || "";
                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() {
        try {
            const textarea = document.querySelector("textarea");
            if (!textarea) return;

            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,
    ) {
        const {
            creatorUrl,
            characterVersion,
            name: metaName,
            creatorNotes,
        } = await getCharacterMeta();

        // Tokenize relevant fields
        const tokenizedDesc = tokenizeField(charBlock, charName, userName);
        const tokenizedScen = tokenizeField(scen, charName, userName);
        const tokenizedExs = tokenizeField(exs, charName, userName);

        /* ----------------------
                 Resolve display name
              ---------------------- */
        let displayName;
        if (useChatNameForName) {
            // Prefer chat_name when toggle is enabled
            displayName = (
                chatData?.character?.chat_name ||
                metaName ||
                chatData?.character?.name ||
                ""
            ).trim();
        } else {
            // Prefer canonical name first
            displayName = (
                metaName ||
                chatData?.character?.name ||
                chatData?.character?.chat_name ||
                ""
            ).trim();
        }
        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}`;
            }
        }

        const includeCreatorNotes =
            localStorage.getItem("includeCreatorNotes") !== "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: [],
                creator: creatorUrl,
                character_version: versionText,
                extensions: {},
            },
        };
    }

    /* ============================
       ==       EXPORTERS       ==
       ============================ */
    async function saveAsJson(charName, charBlock, scen, initMsg, exs, userName) {
        const jsonData = await buildCharaCardV2(
            charName,
            charBlock,
            scen,
            initMsg,
            exs,
            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}.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 cardData = await buildCharaCardV2(
                charName,
                charBlock,
                scen,
                initMsg,
                exs,
                userName,
            );

            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 meta = await getCharacterMeta();
                        const tokens = await getFilenameTokens(meta);
                        const currentTemplate =
                            localStorage.getItem("filenameTemplate") || "{name}";
                        const fileName =
                            buildFilenameFromTemplate(currentTemplate, tokens) || "export";

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

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

            img.src = URL.createObjectURL(avatarBlob);
        } catch (err) {
            console.error("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;
        // Attempt to prefetch chat data before UI makes its own request (disabled)
        // prefetchChatData();

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

        interceptNetwork();
    }

    function handleKeyDown(e) {
        if (!inChats()) return;
        if (
            e.key.toLowerCase() !== "t" ||
            e.ctrlKey ||
            e.metaKey ||
            e.altKey ||
            e.shiftKey
        )
            return;
        if (
            ["INPUT", "TEXTAREA"].includes(document.activeElement.tagName) ||
            document.activeElement.isContentEditable
        )
            return;

        if (!chatData || !chatData.character || !chatData.character.allow_proxy) {
            if (chatData && chatData.character) {
                alert("Proxy disabled — extraction aborted.");
            }
            return;
        }

        viewActive = !viewActive;
        toggleUIState();
    }

    function cleanup() {
        hasInitialized = false;
        const gui = document.getElementById("char-export-gui");
        if (gui) gui.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();

    // Enhanced tooltip prevention
    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();
})();