Isekai ZERO Desktop Layout & Customizer

Desktop layout and chat style customization

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Isekai ZERO Desktop Layout & Customizer
// @namespace    Violentmonkey Scripts
// @version      1.0
// @description  Desktop layout and chat style customization
// @author       Me
// @match        https://www.isekai.world/*
// @match        https://www.isekaizero.ai/*
// @license      MIT
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // CONFIGURATION
    const CONFIG = {
        maxWidth: 1100,
        menuWidth: 90,
        fontFamily: 'Roboto',
        syncInterval: 500,
        storagePrefix: 'isekaiChatStyle_'
    };

    // ROUTE PATTERNS
    const ROUTES = {
        CHAT: /^\/chats\/(?!saved|likes|comments|followings)([a-zA-Z0-9_-]+)\/?$/,
        STORYLINE: /^\/storylines\/[a-zA-Z0-9_-]+\/?$/,
        CHARACTER: /^\/characters\/[a-zA-Z0-9_-]+\/?$/,
        MAIN_NAV: /^\/$|^\/explore.*$|^\/(creation|chats|profile)\/?$/
    };

    // NAVIGATION ITEMS
    const NAV_ITEMS = [
        { id: 'nav-index', alt: 'index tab icon', label: 'Home' },
        { id: 'nav-explore', alt: 'explore tab icon', label: 'Explore' },
        { id: 'nav-creation', alt: 'creation tab icon', label: 'Create' },
        { id: 'nav-chats', alt: 'chats tab icon', label: 'Chats' },
        { id: 'nav-profile', alt: 'profile tab icon', label: 'Profile' }
    ];

    // DEFAULT STYLE SETTINGS
    const DEFAULT_SETTINGS = {
        useDefaultStyle: false,
        aiColor: '#111235',
        userColor: '#0b1428',
        textColor: '#ffffff',
        glowEnabled: true,
        glowColor: '#9949ca',
        bgOpacity: 0.8,
        bgBlur: 10
    };

    // STATE
    let currentChatId = null;
    let currentSettings = null;
    let styleNodes = {};
    let elements = {};

    // UTILITY FUNCTIONS & STORAGE
    function migrateOldSettings() {
        const old = localStorage.getItem('isekaiChatStyle');
        if (old) {
            localStorage.setItem(CONFIG.storagePrefix + 'global', old);
            localStorage.removeItem('isekaiChatStyle');
        }
    }

    function getChatId() {
        const match = window.location.pathname.match(ROUTES.CHAT);
        return match ? match[1] : null;
    }

    function loadSettings(chatId) {
        try {
            const globalStr = localStorage.getItem(CONFIG.storagePrefix + 'global');
            const globalSet = globalStr ? { ...DEFAULT_SETTINGS, ...JSON.parse(globalStr) } : { ...DEFAULT_SETTINGS };

            if (chatId) {
                const chatStr = localStorage.getItem(CONFIG.storagePrefix + chatId);
                if (chatStr) return { ...globalSet, ...JSON.parse(chatStr) };
            }
            return globalSet;
        } catch (e) {
            console.warn('Failed to load settings:', e);
            return { ...DEFAULT_SETTINGS };
        }
    }

    function saveSettings() {
        try {
            if (currentChatId) {
                localStorage.setItem(CONFIG.storagePrefix + currentChatId, JSON.stringify(currentSettings));
            }
        } catch (e) {
            console.warn('Failed to save settings:', e);
        }
    }

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

    function createStyle(css, id) {
        const node = document.createElement('style');
        if (id) node.id = id;
        node.textContent = css;
        document.head.appendChild(node);
        return node;
    }

    function injectFont() {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = `https://fonts.googleapis.com/css2?family=${CONFIG.fontFamily}`;
        document.head.appendChild(link);
    }

    // CSS TEMPLATES
    function getGlobalCSS() {
        // Calculate centered group layout metrics
        const totalWidth = CONFIG.maxWidth + CONFIG.menuWidth;
        const halfTotal = totalWidth / 2;
        const uiOffset = halfTotal - CONFIG.menuWidth;

        return `
            /* Hide the original bottom navigation securely */
            div[style*="bottom: 0px"]:has(img[alt="index tab icon"]) {
                display: none !important;
                opacity: 0 !important;
                pointer-events: none !important;
            }

            /* --- CUSTOM SIDE MENU --- */
            #custom-side-menu {
                position: fixed;
                left: calc(50% - ${halfTotal}px);
                top: 0;
                width: ${CONFIG.menuWidth}px;
                height: 100vh;
                background: linear-gradient(180deg, rgba(17, 18, 60, 1) 0%, rgba(9, 25, 77, 1) 100%);
                box-shadow: rgba(0, 0, 0, 0.35) 0px 6px 10px 0px;
                display: none; /* Controlled by body.has-side-menu */
                flex-direction: column;
                justify-content: flex-start;
                align-items: center;
                gap: 0px;
                z-index: 999999;
                backdrop-filter: blur(10px);
                border-right: 1px solid rgba(102, 126, 234, 0.5);
                padding-top: 2%;
            }

            body.has-side-menu #custom-side-menu {
                display: flex;
            }

            .custom-nav-btn {
                background: none;
                border: none;
                cursor: pointer;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                transition: transform 0.2s ease, background 0.2s ease;
                padding: 24px 0;
                width: 100%;
            }

            .custom-nav-btn:hover { background: rgba(255, 255, 255, 0.05); }
            .custom-nav-btn img { width: 32px; height: 32px; object-fit: cover; margin-bottom: 6px; transition: opacity 0.3s; }
            .custom-nav-label {
                color: white;
                font-family: ${CONFIG.fontFamily};
                font-size: 12px;
                font-weight: 300 !important;
                text-transform: uppercase;
            }

            /* --- RESPONSIVE MAIN UI WRAPPER --- */
            /* No Side Menu */
            .r-1ye8kvj {
                max-width: ${CONFIG.maxWidth}px !important;
                width: 100% !important;
                margin-left: auto !important;
                margin-right: auto !important;
                padding: 0 !important;
                transition: margin 0.3s ease, padding 0.3s ease;
            }

            /* Chat */
            body.is-chat .r-1ye8kvj {
                padding: 0 90px !important;
            }

            /* State 3: Side Menu is visible */
            body.has-side-menu .r-1ye8kvj {
                margin-left: calc(50% - ${uiOffset}px) !important;
                margin-right: auto !important;
                padding: 0 !important;
            }

            /* State 4: Small screen with Side Menu */
            @media (max-width: ${totalWidth}px) {
                body.has-side-menu #custom-side-menu { left: 0; }
                body.has-side-menu .r-1ye8kvj {
                    margin-left: ${CONFIG.menuWidth}px !important;
                    margin-right: 0 !important;
                    width: calc(100% - ${CONFIG.menuWidth}px) !important;
                }
            }

            /* --- WIDGET --- */
            #chat-style-widget {
                position: fixed;
                bottom: 20px;
                right: 20px;
                z-index: 999;
                font-family: ${CONFIG.fontFamily}, sans-serif;
                display: none;
                flex-direction: column;
                align-items: flex-end;
            }

            #chat-style-toggle {
                background: black;
                border: 1px solid rgba(255,255,255,0.2);
                color: white;
                border-radius: 50%;
                cursor: pointer;
                transition: 0.2s;
                width: 48px;
                height: 48px;
                display: flex;
                align-items: center;
                justify-content: center;
                box-shadow: 0 4px 15px rgba(0,0,0,0.5);
                padding: 0;
            }

            #chat-style-toggle:hover {
                background: rgba(20, 20, 20, 1);
            }

            #chat-style-toggle svg { width: 24px; height: 24px; fill: currentColor; }

            #chat-style-panel {
                display: none;
                margin-bottom: 15px;
                background: black;
                border: 1px solid rgba(255,255,255,0.1);
                padding: 20px;
                border-radius: 12px;
                width: 240px;
                backdrop-filter: blur(10px);
                box-shadow: 0 10px 30px rgba(0,0,0,0.8);
                color: #eee;
            }

            .widget-row {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 10px;
                font-size: 12px;
            }

            .widget-row input[type="color"] { background: none; border: none; width: 30px; height: 30px; cursor: pointer; padding: 0; transition: 0.2s; }
            .widget-row input[type="range"] { width: 100px; cursor: pointer; }
            .widget-row input[type="checkbox"] { cursor: pointer; transform: scale(1.2); transition: 0.2s; }

            .widget-btn-grid {
                display: grid;
                grid-template-columns: 1fr 1fr;
                gap: 8px;
                margin-top: 20px;
            }

            .widget-btn {
                background: #333;
                color: #fff;
                border: none;
                padding: 8px 12px;
                border-radius: 6px;
                cursor: pointer;
                font-size: 12px;
                width: 100%;
                transition: 0.2s;
            }

            .widget-btn:hover { background: #555; }
        `;
    }

    function getChatCSS() {
        return `
            .r-49im1t { background-color: black !important; }
            .r-18u37iz { margin: auto !important; }
            textarea { font-family: ${CONFIG.fontFamily} !important; }

            .css-g5y9jx[style*="padding-right: 15px; padding-left: 15px"]:has(.css-g5y9jx[style="flex: 1 1 0%;"]) {
                width: 100% !important;
                right: 0px !important;
                margin: auto !important;
                padding-left: 0px !important;
            }

            .css-g5y9jx [style*="z-index: 100"] { background-color: rgba(0, 0, 0, 0) !important}

            .css-g5y9jx [style*="z-index: 100"]:has(.css-g5y9jx.r-1i6wzkk.r-lrvibr.r-1loqt21.r-1otgn73):has(div[style="box-shadow: rgba(0, 0, 0, 0) 0px 0px 8px; padding: 6px 8px; border-radius: 10px; background-color: rgba(0, 0, 0, 0);"]) {
                background-color: rgba(0, 0, 0, 0.9) !important; border: 1px solid #555 !important;
            }

            .css-146c3p1, .css-1jxf684 { color: #e1e1e1 !important; }
            .css-1jxf684[data-testid="em"] { color: #c1c1c1 !important; }
            .css-g5y9jx { border: none !important; }

            /* AI messages */
            .css-g5y9jx[style*="padding-bottom: 3px; border-bottom-width: 0.5px"] {
                background: linear-gradient(45deg, var(--ai-bg-grad-1) 0%, var(--ai-bg-grad-2) 100%) !important;
                border-radius: 30px 30px 30px 0 !important;
                width: 70% !important;
                transform: translateX(20px) !important;
                padding: 5px 0 5px 20px !important;
                margin-bottom: 20px !important;
                backdrop-filter: blur(var(--chat-blur)) !important;
                -webkit-backdrop-filter: blur(var(--chat-blur)) !important;
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7) !important;
            }

            /* User messages */
            .css-g5y9jx[style*="background-color: rgba(22, 22, 26, 0.8)"] {
                background: linear-gradient(90deg, var(--user-bg-grad-1) 0%, var(--user-bg-grad-2) 100%) !important;
                border-radius: 30px 30px 0 30px !important;
                width: auto !important;
                min-width: 9% !important;
                max-width: 60% !important;
                align-items: normal !important;
                padding-left: 20px !important;
                margin: 0 30px 20px auto !important;
                backdrop-filter: blur(var(--chat-blur)) !important;
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7) !important;
                transform: translateX(-20px) !important;
            }

            .css-1jxf684, .css-146c3p1:not([style*="ionicons"], [style*="material"], [style*="FontAwesome"], [style*="feather"], [style*="Fontisto"], [style*="anticon"]), css-11aywtz, css-g5y9jx, r-6taxm2 {
                font-family: ${CONFIG.fontFamily};
                line-height: 1.6 !important;
            }

            .css-g5y9jx[style*="background-color: rgb(45, 41, 38)"] {
                background-color: rgba(0, 0, 0, 0) !important;
                margin-top: 0px !important;
            }

            .css-g5y9jx[style*="position: absolute; inset: 0px;"] { margin-top: 0px !important; }
            .css-g5y9jx[style*="flex: 1 1 0%; padding: 0 0 0 15px;"] { padding-top: 0px !important; }
            .css-g5y9jx[style*="background-color: rgb(0, 0, 0)"] { background-color: rgba(0, 0, 0, 0) !important; }

            .css-146c3p1[style="color: rgb(0, 0, 0); font-size: 10px; font-weight: bold;"] { color: black !important; }
            .css-146c3p1.r-lrvibr[style*="font-size: 20px; color: rgb(0, 0, 0); font-family: ionicons"] { color: white !important; }

            button:has(.css-146c3p1.r-lrvibr[style*="font-size: 20px; color: rgb(0, 0, 0); font-family: ionicons"]) {
                background-color: transparent !important;
            }

            .css-g5y9jx[data-testid*="blockquote"] {
                background: linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%) !important;
                border-left: 3px solid rgba(255, 255, 255, 0.5) !important;
                margin: 5px 0 !important;
            }

            .css-1jxf684[data-testid="strong"] { font-weight: bold !important; }
            .css-g5y9jx[data-testid*="marked-list-item"] { margin-left: 0 !important; }
            .r-h3f8nf { min-height: 1800px !important; }
            .r-ytbthy { padding-left: 50px !important; padding-right: 50px !important; }

            .css-g5y9jx[style*="align-items: center; z-index: 100; flex-direction: row; padding-top: 0px; position: absolute; top: 0px;"] {
                background: linear-gradient(90deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.6) 100%) !important;
            }

            .css-g5y9jx[style*="position: absolute; inset: 0px; z-index: 100;"] { background-color: rgba(0, 0, 0, 0.1) !important; }
            .css-g5y9jx[style*="margin-bottom: 5px; padding-right: 15px; padding-left: 15px; flex-direction: row; justify-content: space-between; align-items: center;"] { padding-left: 0px !important; }

            /* Text glow and color */
            .css-1jxf684[style*="color: rgb(225, 138, 36)"],
            .css-146c3p1[style*="color: rgb(225, 138, 36)"] {
                color: var(--chat-text-color) !important;
                animation: var(--glow-animation) !important;
                overflow: visible !important;
            }

            @-webkit-keyframes custom-glow {
                from { text-shadow: 0 0 1px var(--glow-grad-1); }
                to { text-shadow: 0 0 2px var(--glow-grad-2); }
            }

            .css-1jxf684.r-lrvibr[data-testid="code"] {
                font-family: Consolas, "Courier New", monospace !important;
                background-color: rgba(0, 0, 0, 0.5) !important;
                padding: 2px 4px !important;
            }

            /* Image fixes */
            .css-g5y9jx img, .r-1ye8kvj img, video {
                width: 100% !important;
                height: 100% !important;
                object-fit: contain !important;
            }

            .css-g5y9jx[style*="z-index: 1"] img { object-fit: cover !important; }

            div.css-g5y9jx[style*="z-index: 1"]:has(img:not([src])),
            div.css-g5y9jx[style*="z-index: 1"]:has(img[src=""]) {
                display: none !important;
            }

            div.css-g5y9jx[style*="z-index: 1"]:has(img:not([src])) ~ div.css-g5y9jx[style*="z-index: 2"] img,
            div.css-g5y9jx[style*="z-index: 1"]:has(img[src=""]) ~ div.css-g5y9jx[style*="z-index: 2"] img {
                object-fit: cover !important;
            }

            button.css-g5y9jx.r-1i6wzkk.r-lrvibr.r-1loqt21.r-1otgn73[style*="padding: 10px 15px; border-width: 1px; border-color: rgb(131, 131, 131)"] {
                border: 1px solid #444 !important;
            }

            div[style="background-color: rgba(0, 0, 0, 0.8); height: 125px; align-items: center; justify-content: flex-end;"] {
                background-color: rgba(0, 0, 0, 0) !important;
            }

            div.css-g5y9jx.r-1uaz6oj.r-qklmqi.r-18u37iz:has(.css-146c3p1.r-jwli3a.r-13awgt0.r-n6v787.r-1sp51qo.r-q4m81j.r-1rvyss1.r-13l2t4g) {
                margin: 0 !important;
            }
        `;
    }

    function getOtherCSS() {
        return `
            .css-g5y9jx[style*="width: 600px"] { width: 100% !important; margin: auto !important; }
            .css-g5y9jx img, .r-1ye8kvj img, video {
                width: 100% !important;
                height: 100% !important;
                object-fit: contain !important;
            }

            .css-g5y9jx[style*="overflow: hidden; position: absolute; inset: 0px; border-radius: 12px; opacity: 0.3;"] img,
            .css-g5y9jx[style*="overflow: hidden; position: absolute; inset: 0px; border-radius: 10px;"] img {
                object-fit: cover !important;
            }

            div.css-g5y9jx[style*="padding: 50px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding: 0px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding-right: 12px; padding-left: 12px; padding-bottom: 80px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding: 15px 12px 120px"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz) {
                align-items: center !important;
            }

            div.css-g5y9jx.r-1awozwy.r-18u37iz.r-1iud8zs.r-1wtj0ep.r-ubg91z.r-13qz1uu { padding-top: 5px !important; }

            div.css-g5y9jx.r-1udh08x.r-xyw6el.r-bnwqim:not(:has(.css-g5y9jx.r-18u37iz.r-1wtj0ep.r-5oul0u)) {
                width: 600px !important;
                margin: auto !important;
            }
        `;
    }

    function getOtherCSS2() {
        return `
            .css-g5y9jx[style*="width: 600px"] { width: 100% !important; margin: auto !important; }
            .css-g5y9jx img, .r-1ye8kvj img, video { object-fit: cover !important; }

            .css-g5y9jx[style*="overflow: hidden; position: absolute; bottom: 20px; left: 0px; right: 0px; height: 300px;"] img {
                object-fit: contain !important;
            }

            div.css-g5y9jx[style*="padding: 50px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding: 0px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding-right: 12px; padding-left: 12px; padding-bottom: 80px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
            div.css-g5y9jx[style*="padding: 15px 12px 120px"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz) {
                align-items: center !important;
            }

            div.css-g5y9jx.r-1awozwy.r-18u37iz.r-1iud8zs.r-1wtj0ep.r-ubg91z.r-13qz1uu { padding-top: 5px !important; }

            div.css-g5y9jx.r-t23y2h.r-zhp00w:has(.css-g5y9jx.r-1awozwy.r-1q9bdsx.r-1pi2tsx.r-1777fci.r-13qz1uu) {
                width: 15vw !important;
            }

            div.css-g5y9jx.r-18u37iz.r-1wtj0ep.r-117bsoe.r-13qz1uu {
                justify-content: center !important;
                gap: 20px;
            }

            div.css-146c3p1.r-8akbws.r-krxsd3.r-dnmrzs.r-1qsk4np.r-1udbk01.r-jwli3a.r-150tq8z.r-1b43r93.r-b88u0q.r-14yzgew,
            h1.css-146c3p1.r-8akbws.r-krxsd3.r-dnmrzs.r-1qsk4np.r-1udbk01.r-jwli3a.r-150tq8z.r-1b43r93.r-b88u0q.r-14yzgew.r-wr9vkk {
                font-weight: normal !important;
            }

            div.css-146c3p1.r-ubezar.r-1kfrs79.r-1bymd8e { font-size: 20px; }
        `;
    }

    // STYLE MANAGEMENT
    function initStyles() {
        injectFont();
        styleNodes.global = createStyle(getGlobalCSS(), 'i0-global-styles');
        styleNodes.chat = createStyle(getChatCSS(), 'i0-chat-styles');
        styleNodes.other = createStyle(getOtherCSS(), 'i0-other-styles');
        styleNodes.other2 = createStyle(getOtherCSS2(), 'i0-other2-styles');
    }

    function getCurrentRouteType() {
        const path = window.location.pathname;
        if (ROUTES.CHAT.test(path)) return 'chat';
        if (ROUTES.STORYLINE.test(path) || ROUTES.CHARACTER.test(path)) return 'content';
        return 'default';
    }

    function updateStyles() {
        const routeType = getCurrentRouteType();
        const isMainNav = ROUTES.MAIN_NAV.test(window.location.pathname);
        const newChatId = getChatId();

        // Control layout and visibility via body classes
        document.body.classList.toggle('has-side-menu', isMainNav);
        document.body.classList.toggle('is-chat', routeType === 'chat');

        if (newChatId !== currentChatId) {
            currentChatId = newChatId;
            currentSettings = loadSettings(currentChatId);
            updateWidgetUI();
            applyStyleVariables();
        }

        styleNodes.chat.disabled = routeType !== 'chat';
        styleNodes.other.disabled = routeType !== 'content';
        styleNodes.other2.disabled = routeType === 'chat' || routeType === 'content';

        updateWidgetVisibility(routeType === 'chat');
    }

    function updateWidgetVisibility(show) {
        if (elements.widget) elements.widget.style.display = show ? 'flex' : 'none';
    }

    function setupRouteListeners() {
        const originalPushState = history.pushState.bind(history);
        const originalReplaceState = history.replaceState.bind(history);

        history.pushState = function(...args) {
            originalPushState(...args);
            updateStyles();
        };

        history.replaceState = function(...args) {
            originalReplaceState(...args);
            updateStyles();
        };

        window.addEventListener('popstate', updateStyles);
    }

    // SIDE MENU
    function createSideMenu() {
        if (elements.menu) return;

        const menu = document.createElement('div');
        menu.id = 'custom-side-menu';

        NAV_ITEMS.forEach(item => {
            const btn = document.createElement('button');
            btn.className = 'custom-nav-btn';

            // Immediately fetch the native element's SRC to avoid broken empty image icons
            const originalImg = document.querySelector(`img[alt="${item.alt}"]`);
            const src = originalImg ? originalImg.src : '';

            btn.innerHTML = `
                <img id="custom-img-${item.id}" src="${src}" alt="${item.label}" ${src ? '' : 'style="opacity: 0;"'}>
                <span class="custom-nav-label">${item.label}</span>
            `;
            btn.addEventListener('click', () => handleNavClick(item.alt));
            menu.appendChild(btn);
        });

        document.body.appendChild(menu);
        elements.menu = menu;
    }

    function handleNavClick(altText) {
        const originalImg = document.querySelector(`img[alt="${altText}"]`);
        if (!originalImg) return;
        const reactButton = originalImg.closest('[role="button"]') || originalImg.closest('button');
        if (reactButton) reactButton.click();
    }

    function syncIcons() {
        if (!elements.menu) return;
        NAV_ITEMS.forEach(item => {
            const originalImg = document.querySelector(`img[alt="${item.alt}"]`);
            const customImg = document.getElementById(`custom-img-${item.id}`);
            if (originalImg && customImg && customImg.src !== originalImg.src) {
                customImg.src = originalImg.src;
                customImg.style.opacity = '1'; // Show if it was previously hidden
            }
        });
    }

    function startIconSync() {
        setInterval(() => {
            // Wait for native app to load its images before we clone and show our menu
            const originalImages = document.querySelectorAll('img[alt$="tab icon"]');
            if (originalImages.length > 0) {
                createSideMenu();
                syncIcons();
            }
        }, CONFIG.syncInterval);
    }

    // STYLE WIDGET & VARIABLES
    function applyStyleVariables() {
        const root = document.documentElement.style;
        const s = currentSettings;

        const bgOpacity = isNaN(parseFloat(s.bgOpacity)) ? 0.8 : parseFloat(s.bgOpacity);
        const bgBlur = isNaN(parseInt(s.bgBlur)) ? 10 : parseInt(s.bgBlur);

        const activeAIHex = s.useDefaultStyle ? '#000000' : s.aiColor;
        const activeUserHex = s.useDefaultStyle ? '#222222' : s.userColor;
        const activeTextHex = s.useDefaultStyle ? '#e19a24' : s.textColor;
        const activeGlowEnabled = s.useDefaultStyle ? false : s.glowEnabled;

        const ai = hexToRgb(activeAIHex);
        const us = hexToRgb(activeUserHex);
        const gl = hexToRgb(s.glowColor);

        root.setProperty('--chat-text-color', activeTextHex);
        root.setProperty('--glow-animation', activeGlowEnabled ? 'custom-glow 1s ease-in-out infinite alternate' : 'none');

        root.setProperty('--ai-bg-grad-1', `rgba(${Math.floor(ai.r * 0.4)}, ${Math.floor(ai.g * 0.4)}, ${Math.floor(ai.b * 0.4)}, 1)`);
        root.setProperty('--ai-bg-grad-2', `rgba(${ai.r}, ${ai.g}, ${ai.b}, ${bgOpacity})`);

        root.setProperty('--user-bg-grad-1', `rgba(${us.r}, ${us.g}, ${us.b}, ${bgOpacity})`);
        root.setProperty('--user-bg-grad-2', `rgba(${us.r}, ${us.g}, ${us.b}, ${Math.min(1, bgOpacity + 0.1) + 0.2})`);

        root.setProperty('--glow-grad-1', `rgba(${Math.floor(gl.r * 0.8)}, ${Math.floor(gl.g * 0.8)}, ${Math.floor(gl.b * 0.8)}, 1)`);
        root.setProperty('--glow-grad-2', `rgba(${gl.r}, ${gl.g}, ${gl.b}, 1)`);

        root.setProperty('--chat-blur', `${bgBlur}px`);

        updateWidgetInputsState(s.useDefaultStyle);
        saveSettings();
    }

    function updateWidgetInputsState(disabled) {
        const inputsToLock = ['wi-aiColor', 'wi-userColor', 'wi-textColor', 'wi-glowColor', 'wi-glowEnabled'];
        inputsToLock.forEach(id => {
            const el = document.getElementById(id);
            if (el) {
                el.disabled = disabled;
                el.style.opacity = disabled ? '0.4' : '1';
                el.style.cursor = disabled ? 'not-allowed' : 'pointer';
            }
        });
    }

    function updateWidgetUI() {
        if (!elements.widget) return;

        const valueInputs = ['aiColor', 'userColor', 'textColor', 'glowColor', 'bgOpacity', 'bgBlur'];
        valueInputs.forEach(key => {
            const el = document.getElementById(`wi-${key}`);
            if (el) el.value = currentSettings[key] !== undefined ? currentSettings[key] : DEFAULT_SETTINGS[key];
        });

        const glowEl = document.getElementById('wi-glowEnabled');
        if (glowEl) glowEl.checked = currentSettings.glowEnabled;

        const useDefEl = document.getElementById('wi-useDefaultStyle');
        if (useDefEl) useDefEl.checked = currentSettings.useDefaultStyle;
    }

    function createWidget() {
        if (elements.widget) return;

        const widget = document.createElement('div');
        widget.id = 'chat-style-widget';
        widget.innerHTML = buildWidgetHTML();

        document.body.appendChild(widget);
        elements.widget = widget;

        attachWidgetListeners();
    }

    function buildWidgetHTML() {
        return `
            <div id="chat-style-panel">
                <div class="widget-row" style="margin-bottom: 20px; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 15px;">
                    <label style="color: #E19A24;">Isekai ZERO Style</label>
                    <input type="checkbox" id="wi-useDefaultStyle">
                </div>
                <div class="widget-row">
                    <label>AI Chat Color</label>
                    <input type="color" id="wi-aiColor">
                </div>
                <div class="widget-row">
                    <label>User Chat Color</label>
                    <input type="color" id="wi-userColor">
                </div>
                <hr style="border: 0.5px solid rgba(255,255,255,0.1); margin: 15px 0;" />
                <div class="widget-row">
                    <label>Dialogue Color</label>
                    <input type="color" id="wi-textColor">
                </div>
                <div class="widget-row">
                    <label>Glow Color</label>
                    <input type="color" id="wi-glowColor">
                </div>
                <div class="widget-row">
                    <label>Enable Text Glow</label>
                    <input type="checkbox" id="wi-glowEnabled">
                </div>
                <hr style="border: 0.5px solid rgba(255,255,255,0.1); margin: 15px 0;" />
                <div class="widget-row">
                    <label>Background Opacity</label>
                    <input type="range" id="wi-bgOpacity" min="0" max="1" step="0.1">
                </div>
                <div class="widget-row">
                    <label>Background Blur</label>
                    <input type="range" id="wi-bgBlur" min="0" max="30" step="1">
                </div>
                <div class="widget-btn-grid">
                    <button class="widget-btn" id="wi-save-global" title="Make these settings the default for all new chats">Save as Global</button>
                    <button class="widget-btn" id="wi-reset" title="Wipe this chat's config and revert back to your Global Default">Revert Chat</button>
                </div>
            </div>
            <button id="chat-style-toggle" title="Customize Chat Style">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
                  <path d="M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"/>
                </svg>
            </button>
        `;
    }

    function attachWidgetListeners() {
        document.getElementById('chat-style-toggle').addEventListener('click', () => {
            const panel = document.getElementById('chat-style-panel');
            panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
        });

        const valueInputs = ['aiColor', 'userColor', 'textColor', 'glowColor', 'bgOpacity', 'bgBlur'];
        valueInputs.forEach(key => {
            document.getElementById(`wi-${key}`).addEventListener('input', (e) => {
                currentSettings[key] = e.target.value;
                applyStyleVariables();
            });
        });

        document.getElementById('wi-glowEnabled').addEventListener('change', (e) => {
            currentSettings.glowEnabled = e.target.checked;
            applyStyleVariables();
        });

        document.getElementById('wi-useDefaultStyle').addEventListener('change', (e) => {
            currentSettings.useDefaultStyle = e.target.checked;
            applyStyleVariables();
        });

        document.getElementById('wi-save-global').addEventListener('click', (e) => {
            localStorage.setItem(CONFIG.storagePrefix + 'global', JSON.stringify(currentSettings));
            const btn = e.target;
            const oldText = btn.textContent;
            btn.textContent = "Saved to Global!";
            setTimeout(() => btn.textContent = oldText, 2000);
        });

        document.getElementById('wi-reset').addEventListener('click', (e) => {
            if (currentChatId) localStorage.removeItem(CONFIG.storagePrefix + currentChatId);
            currentSettings = loadSettings(currentChatId);
            updateWidgetUI();
            applyStyleVariables();

            const btn = e.target;
            const oldText = btn.textContent;
            btn.textContent = "Reverted!";
            setTimeout(() => btn.textContent = oldText, 2000);
        });
    }

    // INITIALIZATION
    function init() {
        migrateOldSettings();
        currentChatId = getChatId();
        currentSettings = loadSettings(currentChatId);

        initStyles();
        setupRouteListeners();
        startIconSync();
        createWidget();

        updateWidgetUI();
        applyStyleVariables();
        updateStyles();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();