Greasy Fork

Greasy Fork is available in English.

Discord Midjourney 参数可视化

在 Discord Midjourney 频道添加一个参数面板...

当前为 2025-05-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Discord Midjourney 参数可视化
// @namespace    https://github.com/cwser
// @version      1.0.3
// @description  在 Discord Midjourney 频道添加一个参数面板...
// @author       cwser
// @match        https://discord.com/*
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    // 参数定义
    let params = {
        prompt: '',
        ar: '1:1',
        stylize: 100,
        weird: 0,
        chaos: 0,
        mode: 'standard',
        version: 'v7',
        speed: 'relax',
        draft: false,
        noPrompt: '',
        cref: [], // 每一项将是 { url: string, weight: string, enabled: boolean }
        sref: [], // 每一项将是 { url: string, weight: string, enabled: boolean } (现在支持独立权重)
        oref: [], // 每一项将是 { url: string, weight: string, enabled: boolean }
        directImages: [], // 每一项将是 { url: string, weight: string, enabled: boolean } (weight is the part after ::)
        iw: 1, // Global image weight for directImages, default 1, range 0-3
        sw: 100, // Global style weight for sref, default 100, range 0-100
        cw: 100, // Global character weight for cref, default 100, range 0-100
        ow: 100, // Global overall weight for oref, default 100, range 0-1000
        tile: false,
        seed: '',
        quality: 1,
        stop: 100,
        visibility: '',
        personalParams: '',
        r: 1,
        includeImagine: false // 用于控制是否添加 /imagine prompt: 前缀
    };

    // 主题相关变量
    let currentThemeMode = 'discord'; // 选项: 'light', 'dark', 'discord', 'system'
    const themeStorageKey = 'mjPanelThemePreference_v3';
    const themeModes = ['light', 'dark', 'discord', 'system'];
    const themeTextMap = {
        'light': '浅色模式',
        'dark': '深色模式',
        'discord': '跟随Discord',
        'system': '跟随系统'
    };
    const sunIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M8 12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 12zm-6.39.261a.5.5 0 0 1 .707.707L3.732 11.26a.5.5 0 0 1-.707-.707L1.61 11.968zM12 8a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5zM4.732 4.739a.5.5 0 0 1-.707-.707L5.439 2.61a.5.5 0 1 1 .707.707L4.732 4.739zM2.61 5.439a.5.5 0 0 1 .707.707L1.61 7.854a.5.5 0 1 1-.707-.707L2.61 5.439zM12 4a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2a.5.5 0 0 1 .5-.5zM4.032 1.61a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 1 1-.707.707L4.032 1.61zM11.26 1.61a.5.5 0 0 1 .707.707l-1.414 1.414a.5.5 0 1 1-.707-.707l1.414-1.414zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0z"/></svg>`;
    const moonIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/></svg>`;
    const discordIconSVG = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.488c-1.54-.83-3.263-1.463-5.086-1.854a.934.934 0 00-1.003.693c-.21 1.207-.662 2.343-1.204 3.392-2.816-.087-4.804-1.53-4.804-1.53s-.103.2-.186.377C7.659 7.992 7.136 9.31 7.136 9.31s-1.482-.532-2.884-1.318c0 0-1.19 3.204 2.42 6.03 0 0-1.806 1.27-3.68 1.744a17.56 17.56 0 003.032 1.22c1.45.463 2.938.717 4.465.717 1.526 0 3.016-.254 4.465-.717.046-.016.09-.03.135-.046l.004-.002c.03-.01.06-.018.09-.027.08-.026.157-.05.237-.078.068-.025.136-.05.203-.076.085-.034.17-.066.253-.1.07-.03.14-.06.208-.09.087-.04.173-.078.26-.118.062-.03.124-.06.185-.09.09-.046.18-.09.268-.14.06-.034.12-.07.178-.1.092-.05.183-.1.273-.156.053-.033.106-.065.158-.1.088-.055.175-.11.26-.17.046-.03.092-.06.137-.093.082-.06.163-.12.242-.185.04-.03.08-.06.118-.09.075-.062.148-.124.22-.188.035-.03.068-.06.103-.09.067-.06.132-.12.196-.183.026-.025.05-.05.076-.075.168-.16.33-.322.488-.488.093-.1.184-.2.27-.3.027-.03.053-.06.078-.09.13-.15.255-.3.375-.456.023-.03.046-.06.067-.09.102-.14.2-.28.293-.42.018-.028.036-.055.052-.083.078-.13.15-.26.218-.39.01-.02.02-.04.03-.06.06-.11.112-.22.162-.33.005-.01.01-.02.015-.03.044-.1.082-.19.118-.29.002-.006.004-.01.005-.016.03-.08.056-.16.08-.24.022-.07.04-.14.056-.21.015-.06.027-.12.038-.18.01-.05.018-.09.025-.14.006-.04.01-.08.014-.12.003-.03.005-.06.006-.09.002-.04.002-.07 0-.11s0-.03-.002-.045c-.06-1.597-.27-3.143-.62-4.618zm-4.603 7.06c-1.232 0-2.232-1.022-2.232-2.282s.998-2.282 2.232-2.282c1.232 0 2.232 1.022 2.232 2.282s-1.002 2.282-2.232-2.282zm-5.94-2.282c0 1.26.998 2.282 2.232 2.282s2.232-1.022 2.232-2.282S10.94 7.026 9.707 7.026c-1.232 0-2.232 1.022-2.232 2.282z"/></svg>`;
    const systemIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5 4a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5zm-.5 7.5A.5.5 0 0 1 4 11V5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-.5.5H4.5z"/><path d="M1.5 2A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13zM1 3.5a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9z"/></svg>`;
    const uploadIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/></svg>`;
    const editIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/><path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/></svg>`;

    const themeIcons = {
        'light': sunIconSVG,
        'dark': moonIconSVG,
        'discord': discordIconSVG,
        'system': systemIconSVG
    };

    let systemThemeMediaQuery = null;
    let discordThemeObserver = null;

    function showToast(message) {
        const toast = document.createElement('div');
        toast.textContent = message;
        toast.className = 'mj-toast';
        document.body.appendChild(toast);
        setTimeout(() => { toast.classList.add('show'); }, 10);
        setTimeout(() => {
            toast.classList.remove('show');
            setTimeout(() => { if (document.body.contains(toast)) document.body.removeChild(toast); }, 300);
        }, 2500);
    }

    function createSettingButton() {
        const button = document.createElement('button');
        button.textContent = 'MJ参数';
        button.id = 'mj-floating-settings-button';
        button.addEventListener('click', toggleControlPanel);
        document.body.appendChild(button);
    }

    function toggleControlPanel() {
        const panel = document.getElementById('mj-control-panel');
        if (panel) {
            panel.classList.toggle('visible');
             if (!panel.classList.contains('visible')) {
                const themeMenu = document.getElementById('theme-options-menu');
                if (themeMenu) themeMenu.style.display = 'none';
            }
        }
    }

    function resetParams() {
        params = {
            prompt: '', ar: '1:1', stylize: 100, weird: 0, chaos: 0, mode: 'standard',
            version: 'v7', speed: 'relax', draft: false, noPrompt: '', cref: [],
            sref: [], oref: [], directImages: [], iw: 1, sw: 100, cw: 100, ow: 100, tile: false, seed: '',
            quality: 1, stop: 100, visibility: '', personalParams: '', r: 1,
            includeImagine: false
        };
        // Ensure arrays are truly new and empty, not just references
        params.directImages = [];
        params.cref = [];
        params.sref = [];
        params.oref = [];
    }

    function updatePromptParams() {
        const { prompt, ar, stylize, weird, chaos, mode, draft, noPrompt, version, speed, tile, seed, quality, stop, visibility, personalParams, includeImagine, iw, sw, cw, ow } = params;
        const { cref, sref, oref, directImages } = params;

        const otherParts = [
            ar ? `--ar ${ar}` : '',
            `--s ${stylize}`,
            weird !== 0 ? `--w ${weird}` : '',
            chaos !== 0 ? `--c ${chaos}` : '',
            mode !== 'standard' ? `--${mode}` : '',
            draft ? '--draft' : '',
            noPrompt ? `--no ${noPrompt}` : '',
            version.startsWith('niji') ? `--niji ${version.replace('niji', '')}` : `--v ${version.replace('v', '')}`,
            speed ? `--${speed}` : '',
            tile ? '--tile' : '',
            seed ? `--seed ${seed}` : '',
            quality !== 1 ? `--q ${quality}` : '',
            stop !== 100 ? `--stop ${stop}` : '',
            visibility ? `--${visibility}` : '',
            personalParams ? `--p ${personalParams}` : '',
            params.r > 1 ? `--r ${params.r}` : ''
        ].filter(Boolean);

        // Modified: Ensures space after :: if weightStr is present
        const formatImageWithWeight = (url, weightValue, prefix, isDirectImage = false) => {
            const weightStr = (typeof weightValue === 'string' || typeof weightValue === 'number') ? String(weightValue).trim() : '';
            // The isDirectImage flag became redundant as both branches were identical.
            // Kept structure for minimal diff, but logic is same.
            if (isDirectImage) {
                return `${url}${weightStr !== '' ? ` :: ${weightStr}` : ''}`;
            }
            return `${url}${weightStr !== '' ? ` :: ${weightStr}` : ''}`;
        };
        
        const directImageUrlsArr = directImages
            .filter(item => item.enabled)
            .map(item => formatImageWithWeight(item.url, item.weight, '', true));
        let directImageSection = directImageUrlsArr.join(' ');

        const enabledCrefs = cref.filter(item => item.enabled);
        let crefSection = '';
        if (enabledCrefs.length > 0) {
            const crefUrlsWithWeights = enabledCrefs.map(item => formatImageWithWeight(item.url, item.weight, ''));
            crefSection = `--cref ${crefUrlsWithWeights.join(' ')}`;
            if (cw !== 100) crefSection += ` --cw ${cw}`;
        }
        
        const enabledSrefs = sref.filter(item => item.enabled);
        let srefSection = '';
        if (enabledSrefs.length > 0) {
            const srefUrlsWithWeights = enabledSrefs.map(item => formatImageWithWeight(item.url, item.weight, ''));
            srefSection = `--sref ${srefUrlsWithWeights.join(' ')}`;
            if (params.sw !== 100) srefSection += ` --sw ${params.sw}`;
        }

        const orefUrls = oref.filter(item => item.enabled).map(item => {
             return `--oref ${formatImageWithWeight(item.url, item.weight, '')}`;
        });
        
        let orefSectionGlobal = '';
        if (oref.filter(item => item.enabled).length > 0 && ow !== 100) {
            orefSectionGlobal = `--ow ${ow}`;
        }

        const promptField = document.getElementById('prompt-params');
        if (promptField) {
            const mainPromptPart = prompt.trim();
            const iwPart = (params.iw !== 1 && typeof params.iw !== 'undefined' && directImageUrlsArr.length > 0) ? `--iw ${params.iw}` : '';

            const allParts = [
                directImageSection, mainPromptPart, iwPart, crefSection, srefSection,
                ...orefUrls, orefSectionGlobal, ...otherParts
            ].filter(Boolean);

            let finalPromptString = allParts.join(' ').trim().replace(/\s+/g, ' ');
            if (includeImagine && finalPromptString) {
                finalPromptString = `/imagine prompt: ${finalPromptString}`;
            }
            promptField.value = finalPromptString;
        }
    }

    function getEffectiveDarkModeState() {
        switch (currentThemeMode) {
            case 'light': return false;
            case 'dark': return true;
            case 'discord': return document.documentElement.classList.contains('theme-dark');
            case 'system': return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
            default: return document.documentElement.classList.contains('theme-dark');
        }
    }

    function applyCurrentTheme() {
        const panel = document.getElementById('mj-control-panel');
        if (!panel) return;
        const effectiveDarkMode = getEffectiveDarkModeState();
        panel.classList.toggle('dark-mode', effectiveDarkMode);
        localStorage.setItem(themeStorageKey, currentThemeMode);
        const themeTriggerIcon = document.getElementById('theme-trigger-icon');
        const themeTriggerText = document.getElementById('theme-trigger-text');
        if (themeTriggerIcon) {
            let iconToShow = themeIcons[currentThemeMode] || sunIconSVG;
            if (currentThemeMode === 'discord' || currentThemeMode === 'system') {
                iconToShow = effectiveDarkMode ? moonIconSVG : sunIconSVG;
            }
            themeTriggerIcon.innerHTML = iconToShow;
        }
        if (themeTriggerText) themeTriggerText.textContent = themeTextMap[currentThemeMode] || '未知主题';
        document.querySelectorAll('#theme-options-menu button').forEach(opt => {
            opt.classList.toggle('active', opt.dataset.theme === currentThemeMode);
        });
        setupDynamicThemeListeners();
    }

    function handleSystemThemeChange() { if (currentThemeMode === 'system') applyCurrentTheme(); }

    function handleDiscordThemeChange(mutationsList) {
         if (currentThemeMode === 'discord') {
            for (const mutation of mutationsList) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
                    applyCurrentTheme(); break;
                }
            }
        }
    }

    function setupDynamicThemeListeners() {
        if (systemThemeMediaQuery) {
            systemThemeMediaQuery.removeEventListener ? systemThemeMediaQuery.removeEventListener('change', handleSystemThemeChange) : systemThemeMediaQuery.removeListener(handleSystemThemeChange);
            systemThemeMediaQuery = null;
        }
        if (discordThemeObserver) {
            discordThemeObserver.disconnect();
            discordThemeObserver = null;
        }
        if (currentThemeMode === 'system' && window.matchMedia) {
            systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
            systemThemeMediaQuery.addEventListener ? systemThemeMediaQuery.addEventListener('change', handleSystemThemeChange) : systemThemeMediaQuery.addListener(handleSystemThemeChange);
        } else if (currentThemeMode === 'discord' && typeof MutationObserver !== "undefined") {
            discordThemeObserver = new MutationObserver(handleDiscordThemeChange);
            discordThemeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
        }
    }

    function createControlPanel() {
        const panel = document.createElement('div');
        panel.id = 'mj-control-panel';
        panel.innerHTML = `
            <div class="panel-header">
                <h3 class="panel-title">Midjourney 参数设置</h3>
                <div class="panel-header-actions">
                    <div style="position:relative;">
                        <button id="theme-dropdown-trigger" title="切换主题模式" class="theme-trigger-btn">
                            <span id="theme-trigger-icon"></span><span id="theme-trigger-text"></span>
                        </button>
                        <div id="theme-options-menu" class="theme-options-menu" style="display:none;">
                            <button data-theme="light" class="theme-option-button">${themeIcons.light} ${themeTextMap.light}</button>
                            <button data-theme="dark" class="theme-option-button">${themeIcons.dark} ${themeTextMap.dark}</button>
                            <button data-theme="discord" class="theme-option-button">${themeIcons.discord} ${themeTextMap.discord}</button>
                            <button data-theme="system" class="theme-option-button">${themeIcons.system} ${themeTextMap.system}</button>
                        </div>
                    </div>
                </div>
            </div>

            <div class="panel-main-content">
                <nav class="panel-tabs">
                    <button class="tab-link active" data-tab="tab-main">主要参数</button>
                    <button class="tab-link" data-tab="tab-references">图像参考</button>
                    <button class="tab-link" data-tab="tab-advanced">高级设置</button>
                </nav>

                <div id="tab-main" class="tab-content active">
                    <div class="form-grid">
                        <div class="form-group">
                            <label for="main-prompt">主要提示词</label>
                            <textarea id="main-prompt" placeholder="输入主要提示词..."></textarea>
                        </div>
                         <div class="form-group">
                            <label for="no-prompt">排除词 (--no)</label>
                            <textarea id="no-prompt" placeholder="输入需要排除的元素..."></textarea>
                        </div>
                        <div class="form-group span-2" id="ar-section">
                            <label>图片尺寸 (--ar)</label>
                            <div class="ar-controls">
                                <div class="ar-preview-container">
                                    <div id="ratio-preview-bg"></div>
                                    <div id="ratio-preview"><div id="ratio-box">1:1</div></div>
                                </div>
                                <div class="ar-slider-group">
                                    <div id="size-buttons" class="btn-group"></div>
                                    <input type="range" id="ratio-slider" min="0" max="10" value="5">
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="stylize">风格化 (--s)</label>
                            <div class="slider-control"><input type="range" id="stylize" min="0" max="1000" value="100" step="50"><span id="stylize-value">100</span></div>
                        </div>
                        <div class="form-group">
                            <label for="chaos">多样性 (--c)</label>
                            <div class="slider-control"><input type="range" id="chaos" min="0" max="100" value="0" step="5"><span id="chaos-value">0</span></div>
                        </div>
                        <div class="form-group">
                            <label for="weird">奇特化 (--w)</label>
                             <div class="slider-control"><input type="range" id="weird" min="0" max="3000" value="0" step="100"><span id="weird-value">0</span></div>
                        </div>
                         <div class="form-group">
                            <label for="version-select">版本 (--v, --niji)</label>
                            <select id="version-select">
                                <option value="v7">v7</option><option value="v6.1">v6.1</option><option value="v6">v6</option>
                                <option value="v5.2">v5.2</option><option value="v5.1">v5.1</option><option value="v5">v5</option>
                                <option value="v4">v4</option><option value="v3">v3</option><option value="v2">v2</option>
                                <option value="v1">v1</option><option value="niji6">niji6</option><option value="niji5">niji5</option><option value="niji4">niji4</option>
                            </select>
                        </div>
                    </div>
                </div>

                <div id="tab-references" class="tab-content">
                    <div class="ref-grid-main">
                        <div class="ref-module" data-type="directImages">
                            <div class="ref-module-header">
                                <h4>图片提示 (--iw)</h4>
                                <div class="global-weight-control">
                                    <label for="iw-slider">整体强度</label>
                                    <div class="weight-slider-mini">
                                        <input type="range" id="iw-slider" min="0" max="3" value="1" step="0.1">
                                        <span id="iw-value">1</span>
                                    </div>
                                </div>
                            </div>
                            <div class="ref-input-section">
                                <input type="text" placeholder="图片URL" class="ref-url-input">
                                <input type="text" placeholder="权重" class="ref-weight-input">
                                <button class="ref-add-btn">添加</button>
                            </div>
                            <div class="ref-container-large" id="directImages-preview"></div>
                        </div>
                        <div class="ref-module" data-type="cref">
                            <div class="ref-module-header">
                                <h4>角色参考 (--cref)</h4>
                                <div class="global-weight-control">
                                    <label for="cw-slider">整体强度</label>
                                    <div class="weight-slider-mini">
                                        <input type="range" id="cw-slider" min="0" max="100" value="100" step="5">
                                        <span id="cw-value">100</span>
                                    </div>
                                </div>
                            </div>
                            <div class="ref-input-section">
                                <input type="text" placeholder="角色图片URL" class="ref-url-input">
                                <input type="number" placeholder="权重" class="ref-weight-input">
                                <button class="ref-add-btn">添加</button>
                            </div>
                            <div class="ref-container-large" id="cref-preview"></div>
                        </div>
                        <div class="ref-module" data-type="sref">
                            <div class="ref-module-header">
                                <h4>风格参考 (--sref)</h4>
                                <div class="global-weight-control">
                                    <label for="sw-slider">整体强度</label>
                                    <div class="weight-slider-mini">
                                        <input type="range" id="sw-slider" min="0" max="100" value="100" step="10"><span id="sw-value">100</span>
                                    </div>
                                </div>
                            </div>
                            <div class="ref-input-section">
                                <input type="text" placeholder="URL、random或数字码" class="ref-url-input">
                                <input type="number" placeholder="权重" class="ref-weight-input">
                                <button class="ref-add-btn">添加</button>
                            </div>
                            <div class="ref-container-large" id="sref-preview"></div>
                        </div>
                        <div class="ref-module" data-type="oref">
                            <div class="ref-module-header">
                                <h4>全方位参考 (--oref)</h4>
                                <div class="global-weight-control">
                                    <label for="ow-slider">整体强度</label>
                                    <div class="weight-slider-mini">
                                        <input type="range" id="ow-slider" min="0" max="1000" value="100" step="50">
                                        <span id="ow-value">100</span>
                                    </div>
                                </div>
                            </div>
                            <div class="ref-input-section">
                                <input type="text" placeholder="参考图片URL" class="ref-url-input">
                                <input type="number" placeholder="权重" class="ref-weight-input">
                                <button class="ref-add-btn">添加</button>
                            </div>
                            <div class="ref-container-large" id="oref-preview"></div>
                        </div>
                    </div>
                </div>

                <div id="tab-advanced" class="tab-content">
                   <div class="form-grid">
                        <div class="form-group">
                            <label>速度</label>
                            <div class="btn-group speed-btn-group">
                                <button data-value="relax" class="speed-btn active">标准</button><button data-value="fast" class="speed-btn">快速</button><button data-value="turbo" class="speed-btn">极速</button>
                            </div>
                        </div>
                        <div class="form-group">
                            <label>模式</label>
                            <div class="btn-group mode-btn-group">
                                <button data-value="standard" class="mode-btn active">标准</button><button data-value="raw" class="mode-btn">原始</button>
                            </div>
                        </div>
                         <div class="form-group">
                            <label>可见性</label>
                            <div class="btn-group visibility-btn-group">
                                <button data-value="" class="visibility-btn active">默认</button><button data-value="public" class="visibility-btn">公开</button><button data-value="stealth" class="visibility-btn">隐身</button>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="seed-input">种子 (--seed)</label>
                            <input type="number" id="seed-input" placeholder="0-4294967295" min="0" max="4294967295">
                        </div>
                        <div class="form-group">
                            <label for="quality-slider">质量 (--q)</label>
                            <div class="slider-control"><input type="range" id="quality-slider" min="0" max="4" step="1" value="2"><span id="quality-value">1</span></div>
                        </div>
                        <div class="form-group">
                            <label for="stop-slider">停止 (--stop)</label>
                            <div class="slider-control"><input type="range" id="stop-slider" min="10" max="100" step="1" value="100"><span id="stop-value">100</span></div>
                        </div>
                        <div class="form-group">
                            <label for="r-slider">批量任务 (--r)</label>
                            <div class="slider-control"><input type="range" id="r-slider" min="1" max="10" value="1"><span id="r-value">1</span></div>
                        </div>
                         <div class="form-group">
                            <label for="personal-params">个性化 (--p)</label>
                            <input type="text" id="personal-params" placeholder="输入个性化参数">
                        </div>
                        <div class="form-group toggle-group">
                            <label for="tile-toggle-switch">重复图案 (--tile)</label>
                            <div class="toggle-switch" id="tile-toggle-switch"><div class="toggle-dot"></div></div>
                        </div>
                         <div class="form-group toggle-group">
                            <label for="draft-toggle-switch">草稿模式 (--draft)</label>
                            <div class="toggle-switch" id="draft-toggle-switch"><div class="toggle-dot"></div></div>
                        </div>
                   </div>
                </div>
            </div>

            <div class="panel-footer">
                <textarea id="prompt-params" placeholder="在此处粘贴Midjourney指令后点击“解析”,或查看最终生成的参数..."></textarea>
                <div class="footer-actions">
                    <div class="toggle-group imagine-toggle">
                        <label for="imagine-toggle-switch">添加 /imagine</label>
                        <div class="toggle-switch" id="imagine-toggle-switch"><div class="toggle-dot"></div></div>
                    </div>
                    <div class="footer-buttons">
                         <button id="parse-btn" class="action-button-secondary">解析</button>
                         <button id="clear-btn" class="action-button-secondary">清空</button>
                         <button id="copy-btn" class="action-button-primary">拷贝</button>
                    </div>
                </div>
            </div>
        `;
        document.body.appendChild(panel);

        const savedTheme = localStorage.getItem(themeStorageKey);
        currentThemeMode = (savedTheme && themeModes.includes(savedTheme)) ? savedTheme : 'discord';

        bindControlEvents();
        applyCurrentTheme();

        document.addEventListener('click', function(event) {
            const themeMenu = document.getElementById('theme-options-menu');
            const themeTrigger = document.getElementById('theme-dropdown-trigger');
            if (themeMenu && themeTrigger && themeMenu.style.display === 'block' && !themeMenu.contains(event.target) && !themeTrigger.contains(event.target)) {
                themeMenu.style.display = 'none';
            }
        });
    }

    function setInitialActiveButtons() {
        const $ = id => document.getElementById(id);
        const buttonGroups = [
            { className: 'speed-btn', param: 'speed' },
            { className: 'mode-btn', param: 'mode' },
            { className: 'visibility-btn', param: 'visibility' }
        ];
        buttonGroups.forEach(group => {
            document.querySelectorAll(`.${group.className}`).forEach(btn => {
                btn.classList.toggle('active', btn.dataset.value === params[group.param]);
            });
        });
        const ratioSlider = $('ratio-slider');
        if (ratioSlider) {
            const sizeMap = ['1:2', '9:16', '2:3', '3:4', '5:6', '1:1', '6:5', '4:3', '3:2', '16:9', '2:1'];
            const ratioIndex = sizeMap.indexOf(params.ar);
            ratioSlider.value = ratioIndex !== -1 ? ratioIndex : 5; 
            ratioSlider.dispatchEvent(new Event('input'));
        }
    }

    function updateToggleVisuals(switchId, property) {
        const switchEl = document.getElementById(switchId);
        if (switchEl) switchEl.classList.toggle('active', !!params[property]);
    }

    function setupRefSection() {
        document.querySelectorAll('.ref-module').forEach(module => {
            const paramKey = module.dataset.type;
            const addBtn = module.querySelector('.ref-add-btn');
            const urlInput = module.querySelector('.ref-url-input');
            const weightInput = module.querySelector('.ref-weight-input');
            const container = module.querySelector('.ref-container-large');

            if (addBtn && urlInput) {
                addBtn.onclick = () => {
                    const urlValue = urlInput.value.trim();
                    if (!urlValue) {
                        showToast(`请输入${getRefTypeDisplayName(paramKey)}URL`);
                        return;
                    }
                    const weightValue = weightInput ? weightInput.value.trim() : '';
                    addReferenceItem(paramKey, urlValue, weightValue);
                    urlInput.value = '';
                    if (weightInput) weightInput.value = '';
                };
            }
            if (container) {
                let overlay = container.querySelector('.drop-overlay');
                if (!overlay) { 
                    overlay = document.createElement('div');
                    overlay.className = 'drop-overlay';
                    overlay.innerHTML = `<div class="drop-overlay-content"><div class="drop-icon">${uploadIconSVG}</div><div class="drop-text">松开即可添加</div></div>`;
                    overlay.style.display = 'none'; 
                    container.appendChild(overlay);
                }
                setupDropZoneEvents(container, paramKey);
            }
        });
    }

    function getRefTypeDisplayName(paramKey) {
        const nameMap = { 'directImages': '图片提示', 'cref': '角色参考', 'sref': '风格参考', 'oref': '全方位参考' };
        return nameMap[paramKey] || paramKey;
    }

    function setupDropZoneEvents(dropZone, paramKey) {
        dropZone.addEventListener('dragover', (e) => { 
            e.preventDefault(); 
            e.stopPropagation(); 
            dropZone.classList.add('drag-over'); 
            showDropMessage(dropZone, "松开即可添加");
        });
        dropZone.addEventListener('dragleave', (e) => { 
            e.preventDefault(); 
            e.stopPropagation(); 
            if (!dropZone.contains(e.relatedTarget)) { 
                dropZone.classList.remove('drag-over'); 
                hideDropMessage(dropZone); 
            } 
        });
        dropZone.addEventListener('drop', (e) => {
            e.preventDefault(); e.stopPropagation();
            dropZone.classList.remove('drag-over'); hideDropMessage(dropZone);
            const url = e.dataTransfer.getData('text/uri-list') || e.dataTransfer.getData('text/plain');
            if (!url) { showToast('无法获取拖拽内容的URL'); return; }
            addReferenceItem(paramKey, url, '');
        });
    }

    function showDropMessage(container, messageText) {
        const overlay = container.querySelector('.drop-overlay');
        if (overlay) {
            overlay.style.display = 'flex';
        }
    }

    function hideDropMessage(container) {
        const overlay = container.querySelector('.drop-overlay');
        if (overlay) {
            overlay.style.display = 'none';
        }
    }

    function addReferenceItem(paramKey, urlValue, weightValue = '') {
        let itemUrl = urlValue;
        let itemWeight = weightValue.trim();

        if (paramKey === 'directImages') {
            if (itemWeight !== '' && !/^\d*\.?\d*$/.test(itemWeight)) {
                showToast('图片独立权重必须是数字 (例如 0.5, 1, 2) 或留空'); return;
            }
        }
        
        const isImageUrl = /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(itemUrl);
        const isSrefCode = paramKey === 'sref' && (/^\d+$/.test(itemUrl) || itemUrl.toLowerCase() === 'random');

        if (paramKey !== 'sref' && !isImageUrl) { showToast('请输入有效的图片URL'); return; }
        if (paramKey === 'sref' && !isImageUrl && !isSrefCode) { showToast("sref请输入有效图片URL, 'random'或数字代码"); return; }

        const targetArray = params[paramKey];
        if (!Array.isArray(targetArray)) return;
        const checkUrl = (paramKey === 'sref' && isSrefCode) ? itemUrl.toLowerCase() : itemUrl;

        if (!targetArray.some(item => item.url === checkUrl && item.weight === itemWeight)) {
            const newItem = { url: checkUrl, weight: itemWeight, enabled: true };
            targetArray.push(newItem);
            addPreviewItem(paramKey, newItem);
            updatePromptParams();
            showToast(`已添加 ${getRefTypeDisplayName(paramKey)}`);
        } else {
            showToast(`该${getRefTypeDisplayName(paramKey)}已添加`);
        }
    }

    function showWeightEditDialog(item, paramKey, previewItem) {
        const dialog = document.createElement('div');
        dialog.className = 'weight-edit-dialog';
        const currentWeight = item.weight || '';
        dialog.innerHTML = `
            <div class="weight-edit-content">
                <h4>编辑权重</h4>
                <div class="weight-edit-input-group">
                    <label>权重值:</label>
                    <input type="text" class="weight-edit-input" value="${currentWeight}" placeholder="留空为默认权重">
                </div>
                <div class="weight-edit-buttons">
                    <button class="weight-edit-cancel">取消</button>
                    <button class="weight-edit-save">保存</button>
                </div>
            </div>
            <div class="weight-edit-overlay"></div>`;
        document.body.appendChild(dialog);
        const input = dialog.querySelector('.weight-edit-input');
        const saveBtn = dialog.querySelector('.weight-edit-save');
        const cancelBtn = dialog.querySelector('.weight-edit-cancel');
        const overlay = dialog.querySelector('.weight-edit-overlay');
        input.focus(); input.select();
        const closeDialog = () => { if (document.body.contains(dialog)) document.body.removeChild(dialog); };
        const saveWeight = () => {
            const newWeight = input.value.trim();
            if (newWeight !== '' && !/^\d*\.?\d*$/.test(newWeight)) {
                showToast('权重必须是数字 (例如 0.5, 1, 2) 或留空'); return;
            }
            item.weight = newWeight;
            updatePreviewItemWeight(previewItem, item, paramKey);
            updatePromptParams(); closeDialog(); showToast('权重已更新');
        };
        saveBtn.onclick = saveWeight;
        cancelBtn.onclick = closeDialog;
        overlay.onclick = closeDialog;
        input.addEventListener('keypress', (e) => { if (e.key === 'Enter') saveWeight(); });
        dialog.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeDialog(); });
    }

    function updatePreviewItemWeight(previewItem, item, paramKey) {
        const weightDisplay = previewItem.querySelector('.weight-large');
        if (weightDisplay) {
            if (item.weight && item.weight.trim() !== '') {
                let prefix = "::";
                weightDisplay.textContent = `${prefix}${item.weight}`;
                weightDisplay.style.display = 'block';
            } else {
                weightDisplay.style.display = 'none';
            }
        }
    }

    function updateAllUIElements() {
        const $ = id => document.getElementById(id);
    
        if ($('main-prompt')) $('main-prompt').value = params.prompt;
        if ($('no-prompt')) $('no-prompt').value = params.noPrompt;
    
        const sliderConfigs = [
            { key: 'stylize', sliderId: 'stylize', displayId: 'stylize-value' },
            { key: 'weird', sliderId: 'weird', displayId: 'weird-value' },
            { key: 'chaos', sliderId: 'chaos', displayId: 'chaos-value' },
            { key: 'iw', sliderId: 'iw-slider', displayId: 'iw-value' },
            { key: 'sw', sliderId: 'sw-slider', displayId: 'sw-value' },
            { key: 'cw', sliderId: 'cw-slider', displayId: 'cw-value' },
            { key: 'ow', sliderId: 'ow-slider', displayId: 'ow-value' },
            { key: 'stop', sliderId: 'stop-slider', displayId: 'stop-value' },
            { key: 'r', sliderId: 'r-slider', displayId: 'r-value' }
        ];
        sliderConfigs.forEach(conf => {
            const slider = $(conf.sliderId); const display = $(conf.displayId);
            if (slider) {
                 const sliderMax = parseFloat(slider.max);
                 slider.value = Math.min(params[conf.key] !== undefined ? params[conf.key] : parseFloat(slider.defaultValue) , sliderMax); // Added default for safety
            }
            if (display) display.textContent = slider ? slider.value : (params[conf.key] !== undefined ? params[conf.key] : '');
        });
    
        const qualityMap = [0.25, 0.5, 1, 2, 4];
        const qualitySlider = $('quality-slider'); const qualityValue = $('quality-value');
        if (qualitySlider && qualityValue) {
            const currentQuality = parseFloat(params.quality);
            const idx = qualityMap.indexOf(currentQuality);
            qualitySlider.value = idx !== -1 ? idx : qualityMap.indexOf(1); // Default to 1 if not found
            qualityValue.textContent = qualityMap[qualitySlider.value];
        }
    
        if ($('version-select')) $('version-select').value = params.version;
        if ($('seed-input')) $('seed-input').value = params.seed;
        if ($('personal-params')) $('personal-params').value = params.personalParams;
    
        const ratioSlider = $('ratio-slider');
        const sizeMap = ['1:2', '9:16', '2:3', '3:4', '5:6', '1:1', '6:5', '4:3', '3:2', '16:9', '2:1'];
        if (ratioSlider) {
            const ratioIndex = sizeMap.indexOf(params.ar);
            ratioSlider.value = ratioIndex !== -1 ? ratioIndex : sizeMap.indexOf('1:1');
            ratioSlider.dispatchEvent(new Event('input')); // Trigger update of preview
        }
    
        setInitialActiveButtons(); 

        updateToggleVisuals('tile-toggle-switch', 'tile');
        updateToggleVisuals('draft-toggle-switch', 'draft');
        updateToggleVisuals('imagine-toggle-switch', 'includeImagine');
    
        refreshPreviews(); 
        updatePromptParams();
    }

    // MODIFIED parseRefUrlsWithWeights function
    const parseRefUrlsWithWeights = (valueString) => {
        const items = [];
        // 1. 规范化处理:将 "url :: weight" 或 "url:: weight" 或 "url ::weight" 统一替换为 "url::weight"
        const normalizedValueString = valueString.replace(/\s*::\s*/g, '::');

        // 2. 按一个或多个空格分割成 token 数组,每个 token 可能是 "url" 或 "url::weight"
        const tokens = normalizedValueString.split(/\s+/).filter(t => t.trim() !== '');

        tokens.forEach(token => {
            const parts = token.split('::');
            const urlOrCode = parts[0].trim();
            let weight = '';

            if (parts.length > 1 && parts[1].trim() !== '') {
                weight = parts[1].trim();
            }

            if (urlOrCode) {
                const isSrefSpecific = (urlOrCode.toLowerCase() === 'random' || /^\d+$/.test(urlOrCode));
                const looksLikeUrl = urlOrCode.match(/^https?:\/\//i) || urlOrCode.includes('.');

                if (looksLikeUrl || isSrefSpecific) {
                     items.push({ url: urlOrCode, weight: weight, enabled: true });
                }
            }
        });
        return items;
    };

    function parseAndApplyMidjourneyCommand() {
        const $ = id => document.getElementById(id);
        const commandInput = $('prompt-params');
        if (!commandInput) return;
        let command = commandInput.value.trim();
    
        if (!command) {
            showToast("请输入Midjourney指令进行解析");
            return;
        }
    
        resetParams(); // Reset params to default before parsing a new command
    
        let remainingCommand = command;
        // Remove /imagine prefix
        remainingCommand = remainingCommand.replace(/^\/(imagine|i)\s*(prompt:)?\s*/i, '').trim();
    
        // Create a mutable copy of default params to populate
        const newParams = JSON.parse(JSON.stringify(params)); // Deep copy
    
        const extractParamWithValue = (regex, processor, isFlag = false) => {
            const match = remainingCommand.match(regex);
            if (match) {
                if (isFlag) {
                    processor(true, newParams);
                } else {
                    processor(match, newParams);
                }
                remainingCommand = remainingCommand.replace(regex, '').trim();
                return true;
            } else if (isFlag) {
                 processor(false, newParams); // Ensure flags are set to false if not present
            }
            return false;
        };
        
        // --oref parsing (handles multiple --oref flags, each with one URL::WEIGHT)
        const orefItems = [];
        remainingCommand = remainingCommand.replace(/--oref\s+([^\s]+(?:\s*::\s*\S+)?)/gi, (match, content) => {
            const parts = content.split('::');
            orefItems.push({ url: parts[0].trim(), weight: (parts[1] || '').trim(), enabled: true });
            return ''; 
        }).trim();
        if (orefItems.length > 0) newParams.oref = orefItems;

        // Parameter extraction (order can matter for overlapping patterns)
        extractParamWithValue(/--iw\s+(\d*\.?\d+)/i, (m, p) => p.iw = parseFloat(m[1]));
        extractParamWithValue(/--sw\s+(\d+)/i, (m, p) => p.sw = Math.min(parseInt(m[1], 10), 1000)); // sref --sw can go up to 1000
        extractParamWithValue(/--cw\s+(\d+)/i, (m, p) => p.cw = parseInt(m[1], 10));
        extractParamWithValue(/--ow\s+(\d+)/i, (m, p) => p.ow = parseInt(m[1], 10));
    
        extractParamWithValue(/--ar\s+([\d:]+)/i, (m, p) => p.ar = m[1]);
        extractParamWithValue(/--(v|version)\s+([a-zA-Z0-9.]+)/i, (m, p) => p.version = 'v' + m[2].replace(/^v/i, ''));
        extractParamWithValue(/--niji\s+([a-zA-Z0-9.]+)/i, (m, p) => p.version = 'niji' + m[1].replace(/^niji/i, ''));
        extractParamWithValue(/--s\s+(\d+)/i, (m, p) => p.stylize = parseInt(m[1], 10));
        extractParamWithValue(/--w\s+(\d+)/i, (m, p) => p.weird = parseInt(m[1], 10));
        extractParamWithValue(/--c\s+(\d+)/i, (m, p) => p.chaos = parseInt(m[1], 10));
        extractParamWithValue(/--q\s+(\d*\.?\d+)/i, (m, p) => {
            const val = parseFloat(m[1]);
            const qualityMapVals = [0.25, 0.5, 1, 2, 4]; // v5/v6 qualities
            if (qualityMapVals.includes(val)) p.quality = val;
        });
        extractParamWithValue(/--seed\s+(\d+)/i, (m, p) => p.seed = m[1]);
        extractParamWithValue(/--stop\s+(\d+)/i, (m, p) => p.stop = parseInt(m[1], 10));
        extractParamWithValue(/--p\s+((?:[^\s"-][^\s-]*|[^\s-]*[^\s"-])[^\s-]*(?:\s+(?:[^\s"-][^\s-]*|[^\s-]*[^\s"-])[^\s-]*)*)/i, (m, p) => p.personalParams = m[1].trim());
        extractParamWithValue(/--r\s+(\d+)/i, (m, p) => p.r = parseInt(m[1], 10));

        // --no parameter (needs to be greedy but not over param flags)
        const noMatch = remainingCommand.match(/--no\s+((?:(?!--(?:ar|v|s|w|c|q|seed|stop|tile|draft|iw|sw|cw|ow|cref|sref|oref|p|r|niji|fast|turbo|relax|raw|public|stealth)\b)[\s\S])+)/i);
        if (noMatch) {
            newParams.noPrompt = noMatch[1].trim();
            remainingCommand = remainingCommand.replace(noMatch[0], '').trim();
        }
    
        // Flags
        extractParamWithValue(/--tile\b/i, (val, p) => p.tile = val, true);
        extractParamWithValue(/--draft\b/i, (val, p) => p.draft = val, true);
        
        // Mode & Speed & Visibility (mutually exclusive for their groups)
        if (extractParamWithValue(/--raw\b/i, (val, p) => { if(val) p.mode = 'raw'; }, true)) {} 
        else { newParams.mode = 'standard'; } // Default if not raw

        if (extractParamWithValue(/--fast\b/i, (val, p) => { if(val) p.speed = 'fast';}, true)) {}
        else if (extractParamWithValue(/--turbo\b/i, (val, p) => {if(val) p.speed = 'turbo';}, true)) {}
        else if (extractParamWithValue(/--relax\b/i, (val, p) => {if(val) p.speed = 'relax';}, true)) {}
        else { newParams.speed = 'relax'; } // Default speed
    
        if (extractParamWithValue(/--public\b/i, (val, p) => {if(val) p.visibility = 'public';}, true)) {}
        else if (extractParamWithValue(/--stealth\b/i, (val, p) => {if(val) p.visibility = 'stealth';}, true)) {}
        else { newParams.visibility = '';} // Default visibility

        // --cref and --sref using the MODIFIED parseRefUrlsWithWeights
        const crefMatch = remainingCommand.match(/--cref\s+((?:(?!--(?:cw|sw|ow|ar|v|s|w|c|q|seed|stop|tile|draft|iw|p|r|niji|fast|turbo|relax|raw|public|stealth|no|sref|oref)\b)[\s\S])+)/i);
        if (crefMatch) {
            newParams.cref = parseRefUrlsWithWeights(crefMatch[1].trim());
            remainingCommand = remainingCommand.replace(crefMatch[0], '').trim();
        }

        const srefMatch = remainingCommand.match(/--sref\s+((?:(?!--(?:cw|sw|ow|ar|v|s|w|c|q|seed|stop|tile|draft|iw|p|r|niji|fast|turbo|relax|raw|public|stealth|no|cref|oref)\b)[\s\S])+)/i);
        if (srefMatch) {
            newParams.sref = parseRefUrlsWithWeights(srefMatch[1].trim());
            remainingCommand = remainingCommand.replace(srefMatch[0], '').trim();
        }
    
        // MODIFIED Direct image parsing logic
        remainingCommand = remainingCommand.replace(/\s+/g, ' ').trim();
        const promptParts = remainingCommand.split(' ');
        newParams.directImages = []; // Ensure it's clean for this parse run
        let promptStartIndex = 0;

        let currentGlobalIndex = 0;
        while(currentGlobalIndex < promptParts.length) {
            const part = promptParts[currentGlobalIndex];
            let consumedPartsCount = 0;
            let imageUrl = '';
            let imageWeight = '';
            let successfullyParsedAsImage = false;

            if (part.toLowerCase().startsWith('http://') || part.toLowerCase().startsWith('https://')) {
                if (part.includes('::')) {
                    const splitByDoubleColon = part.split('::');
                    if (splitByDoubleColon.length === 2) {
                        const potentialUrl = splitByDoubleColon[0];
                        const potentialWeight = splitByDoubleColon[1];
                        if (potentialUrl.trim() !== '' && (/^\d*\.?\d*$/.test(potentialWeight) || potentialWeight === '')) {
                            imageUrl = potentialUrl;
                            imageWeight = potentialWeight.trim();
                            consumedPartsCount = 1;
                            successfullyParsedAsImage = true;
                        }
                    }
                    if (!successfullyParsedAsImage && (splitByDoubleColon[0].toLowerCase().startsWith('http://') || splitByDoubleColon[0].toLowerCase().startsWith('https://'))) {
                        imageUrl = part;
                        imageWeight = '';
                        consumedPartsCount = 1;
                        successfullyParsedAsImage = true;
                    }
                }
                else if (currentGlobalIndex + 2 < promptParts.length &&
                         promptParts[currentGlobalIndex + 1] === "::" &&
                         (/^\d*\.?\d*$/.test(promptParts[currentGlobalIndex + 2]) || promptParts[currentGlobalIndex + 2] === '')) {
                    imageUrl = part;
                    imageWeight = promptParts[currentGlobalIndex + 2].trim();
                    consumedPartsCount = 3;
                    successfullyParsedAsImage = true;
                }
                else if (currentGlobalIndex + 1 < promptParts.length &&
                         promptParts[currentGlobalIndex + 1].startsWith("::")) {
                    const potentialWeightPart = promptParts[currentGlobalIndex + 1].substring(2);
                    if (/^\d*\.?\d*$/.test(potentialWeightPart) || potentialWeightPart === '') {
                        imageUrl = part;
                        imageWeight = potentialWeightPart.trim();
                        consumedPartsCount = 2;
                        successfullyParsedAsImage = true;
                    }
                }

                if (!successfullyParsedAsImage && (part.toLowerCase().startsWith('http://') || part.toLowerCase().startsWith('https://'))) {
                    imageUrl = part;
                    imageWeight = '';
                    consumedPartsCount = 1;
                    successfullyParsedAsImage = true;
                }

                if (successfullyParsedAsImage && imageUrl) {
                    newParams.directImages.push({ url: imageUrl, weight: imageWeight, enabled: true });
                    promptStartIndex += consumedPartsCount;
                    currentGlobalIndex += consumedPartsCount;
                } else {
                    break; 
                }
            } else {
                break; 
            }
        }
        newParams.prompt = promptParts.slice(promptStartIndex).join(' ').trim();
    
        // Assign all parsed values to the global params object
        Object.assign(params, newParams); 
    
        updateAllUIElements(); // Refresh UI with parsed values
        showToast("指令解析完成并已填充参数!");
    }

    function bindControlEvents() {
        const $ = id => document.getElementById(id);

        document.querySelectorAll('.tab-link').forEach(button => {
            button.addEventListener('click', () => {
                document.querySelectorAll('.tab-link').forEach(btn => btn.classList.remove('active'));
                button.classList.add('active');
                document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
                $(button.dataset.tab).classList.add('active');
            });
        });

        const themeTriggerButton = $('theme-dropdown-trigger');
        const themeOptionsMenu = $('theme-options-menu');
        if (themeTriggerButton && themeOptionsMenu) {
            themeTriggerButton.addEventListener('click', (e) => { e.stopPropagation(); themeOptionsMenu.style.display = themeOptionsMenu.style.display === 'none' ? 'block' : 'none'; });
            document.querySelectorAll('.theme-option-button').forEach(button => {
                button.addEventListener('click', () => { currentThemeMode = button.dataset.theme; applyCurrentTheme(); themeOptionsMenu.style.display = 'none'; });
            });
        }

        if ($('main-prompt')) $('main-prompt').oninput = e => { params.prompt = e.target.value; updatePromptParams(); };
        if ($('no-prompt')) $('no-prompt').oninput = e => { params.noPrompt = e.target.value.trim(); updatePromptParams(); };

        const bindSlider = (id, property, displayId, isFloat = false, callback) => {
            const slider = $(id); if (!slider) return;
            // Initialize slider from params or its default value if param is undefined
            const initialValue = params[property] !== undefined ? params[property] : parseFloat(slider.defaultValue);
            const sliderMax = parseFloat(slider.max);
            slider.value = Math.min(initialValue, sliderMax);
            if (displayId && $(displayId)) $(displayId).textContent = slider.value;

            slider.oninput = e => {
                params[property] = isFloat ? parseFloat(e.target.value) : parseInt(e.target.value, 10);
                if (displayId && $(displayId)) $(displayId).textContent = e.target.value;
                if (callback) callback(params[property]);
                updatePromptParams();
            };
        };
        bindSlider('stylize', 'stylize', 'stylize-value');
        bindSlider('weird', 'weird', 'weird-value');
        bindSlider('chaos', 'chaos', 'chaos-value');
        bindSlider('stop-slider', 'stop', 'stop-value');
        bindSlider('r-slider', 'r', 'r-value');
        bindSlider('iw-slider', 'iw', 'iw-value', true);
        bindSlider('sw-slider', 'sw', 'sw-value');
        bindSlider('cw-slider', 'cw', 'cw-value');
        bindSlider('ow-slider', 'ow', 'ow-value');

        const qualitySlider = $('quality-slider'); const qualityValueDisplay = $('quality-value');
        const qualityMap = [0.25, 0.5, 1, 2, 4]; 
        if(qualitySlider && qualityValueDisplay) { 
            // Initial value setting for quality slider is handled in updateAllUIElements
            qualitySlider.oninput = e => {
                params.quality = qualityMap[parseInt(e.target.value, 10)];
                qualityValueDisplay.textContent = params.quality;
                updatePromptParams();
            };
        }

        const bindRadio = (className, property) => {
            document.querySelectorAll(`.${className}`).forEach(btn => {
                btn.addEventListener('click', () => {
                    document.querySelectorAll(`.${className}`).forEach(b => b.classList.remove('active'));
                    btn.classList.add('active'); params[property] = btn.dataset.value; updatePromptParams();
                });
            });
        };
        bindRadio('speed-btn', 'speed'); bindRadio('mode-btn', 'mode'); bindRadio('visibility-btn', 'visibility');

        const bindToggle = (id, property) => {
            const toggle = $(id); if (!toggle) return;
            // Initial visual state is handled in updateAllUIElements
            toggle.addEventListener('click', () => { params[property] = !params[property]; updateToggleVisuals(id, property); updatePromptParams(); });
        };
        bindToggle('tile-toggle-switch', 'tile'); bindToggle('draft-toggle-switch', 'draft'); bindToggle('imagine-toggle-switch', 'includeImagine');

        if ($('version-select')) { 
             $('version-select').onchange = e => { params.version = e.target.value; updatePromptParams(); };
        }
        if ($('seed-input')) { 
            $('seed-input').oninput = e => {
                const value = e.target.value.trim();
                params.seed = (/^\d*$/.test(value) && (value === '' || (parseInt(value) >= 0 && parseInt(value) <= 4294967295))) ? value : params.seed;
                e.target.value = params.seed; // Ensure input reflects validated value
                updatePromptParams();
            };
        }
        if ($('personal-params')) { 
            $('personal-params').oninput = e => { params.personalParams = e.target.value.trim(); updatePromptParams(); };
        }

        const legacyCopy = (el) => { try { document.execCommand('copy'); showToast('参数已复制 (兼容模式)!'); } catch (err) { showToast('复制失败!'); } };
        if ($('copy-btn')) $('copy-btn').onclick = () => {
            const textarea = $('prompt-params'); if (!textarea || !textarea.value) { showToast('没有参数可以拷贝'); return; }
            textarea.select(); textarea.setSelectionRange(0, 99999); // For mobile devices
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(textarea.value).then(() => showToast('参数已复制!')).catch(() => legacyCopy(textarea));
            } else { legacyCopy(textarea); }
        };

        const sizeMap = ['1:2', '9:16', '2:3', '3:4', '5:6', '1:1', '6:5', '4:3', '3:2', '16:9', '2:1'];
        const ratioPresets = { '1:2':{w:50,h:100}, '9:16':{w:56.25,h:100}, '2:3':{w:66.67,h:100}, '3:4':{w:75,h:100}, '5:6':{w:83.33,h:100}, '1:1':{w:100,h:100}, '6:5':{w:100,h:83.33}, '4:3':{w:100,h:75}, '3:2':{w:100,h:66.67}, '16:9':{w:100,h:56.25}, '2:1':{w:100,h:50} };
        const ratioSlider = $('ratio-slider');
        if (ratioSlider) { 
            // Initial value for ratio slider is set in updateAllUIElements
            ratioSlider.oninput = e => {
                const ratio = sizeMap[+e.target.value] || '1:1'; params.ar = ratio;
                const box = $('ratio-box'); const bgBox = $('ratio-preview-bg'); const preset = ratioPresets[ratio];
                if(box && preset && bgBox) { 
                    // Scale preview to fit 100x100 container while maintaining aspect ratio
                    const containerSize = 100;
                    let displayW = preset.w;
                    let displayH = preset.h;
                    if (displayW > containerSize || displayH > containerSize) {
                        if (displayW/displayH > 1) { // Wider than tall
                            displayH = containerSize * (displayH / displayW);
                            displayW = containerSize;
                        } else { // Taller than wide or square
                            displayW = containerSize * (displayW / displayH);
                            displayH = containerSize;
                        }
                    }
                    box.style.width = `${displayW-4}px`; // Account for border
                    box.style.height = `${displayH-4}px`; // Account for border
                    box.textContent = ratio; 
                }
                document.querySelectorAll('#size-buttons button').forEach(btn => btn.classList.toggle('active', btn.dataset.value === ratio));
                updatePromptParams();
            };
        }

        const sizeButtonGroup = $('size-buttons');
        if (sizeButtonGroup) {
            sizeButtonGroup.innerHTML = ''; // Clear previous buttons if any
            const presetMap = { '纵向': '2:3', '正方形': '1:1', '横向': '3:2' }; // Common presets
            Object.entries(presetMap).forEach(([label, ratio]) => {
                const btn = document.createElement('button'); btn.textContent = label; btn.dataset.value = ratio;
                btn.onclick = () => {
                    if (ratioSlider) { 
                        const idx = sizeMap.indexOf(ratio); 
                        if (idx !== -1) { 
                            ratioSlider.value = idx; 
                            ratioSlider.dispatchEvent(new Event('input')); // Trigger slider's input event
                        } 
                    }
                };
                sizeButtonGroup.appendChild(btn);
            });
        }
        
        if ($('clear-btn')) $('clear-btn').onclick = () => {
            resetParams(); 
            updateAllUIElements(); // Refresh UI to default state
            showToast('所有参数已重置为默认值');
        };

        if ($('parse-btn')) $('parse-btn').onclick = parseAndApplyMidjourneyCommand;

        setupRefSection(); 
    }

    function addPreviewItem(paramKey, item) {
        const container = document.getElementById(`${paramKey}-preview`);
        if (!container) return;
        const isSrefCode = paramKey === 'sref' && (item.url === 'random' || /^\d+$/.test(item.url));
        const previewItem = document.createElement('div');
        previewItem.className = `ref-item-large ${isSrefCode ? 'code-item' : 'image-item'}`;
        if (typeof item.enabled !== 'undefined' && !item.enabled) {
             previewItem.classList.add('disabled');
        } else if (typeof item.enabled === 'undefined') { // Default to enabled if not specified
            item.enabled = true;
        }

        let contentHtml = isSrefCode ? `<div class="ref-code-large">${item.url}</div>`
                                     : `<img src="${item.url}" class="ref-image-large" alt="参考图" onerror="this.parentElement.classList.add('load-error'); this.parentElement.innerHTML='<div class=\\'ref-error-large\\'>加载失败</div>';">`;
        
        let weightText = '';
        if (item.weight && item.weight.trim() !== '') {
            let prefix = "::"; // For display consistency
            weightText = `${prefix}${item.weight}`;
        }
        const weightDisplay = `<span class="weight-large" style="${weightText ? '' : 'display:none;'}">${weightText}</span>`;

        previewItem.innerHTML = `
            ${contentHtml}
            ${weightDisplay}
            <button class="ref-edit-large" title="编辑权重">${editIconSVG}</button>
            <button class="ref-toggle-large ${item.enabled ? 'active' : ''}" title="启用/禁用">●</button>
            <button class="ref-delete-large" title="删除">×</button>`;
        container.appendChild(previewItem);

        const editBtn = previewItem.querySelector('.ref-edit-large');
        const toggleBtn = previewItem.querySelector('.ref-toggle-large');
        const deleteBtn = previewItem.querySelector('.ref-delete-large');

        if (editBtn) editBtn.onclick = (e) => { e.stopPropagation(); if (previewItem.classList.contains('load-error')) return; showWeightEditDialog(item, paramKey, previewItem); };
        if (toggleBtn) toggleBtn.onclick = (e) => {
            e.stopPropagation(); if (previewItem.classList.contains('load-error')) return;
            item.enabled = !item.enabled;
            toggleBtn.classList.toggle('active', item.enabled); previewItem.classList.toggle('disabled', !item.enabled);
            updatePromptParams();
        };
        if (deleteBtn) deleteBtn.onclick = (e) => {
            e.stopPropagation();
            const targetArray = params[paramKey];
            const index = targetArray.findIndex(i => i.url === item.url && i.weight === item.weight); // Find by URL and weight
            if (index !== -1) { targetArray.splice(index, 1); previewItem.remove(); updatePromptParams(); }
        };
    }

    function refreshPreviews() {
        const refTypes = ['directImages', 'cref', 'sref', 'oref'];
        refTypes.forEach(paramKey => {
            const container = document.getElementById(`${paramKey}-preview`);
            if (container) {
                container.innerHTML = ''; // Clear existing previews
                const items = params[paramKey]; 
                if (items && Array.isArray(items)) {
                    items.forEach(item => { 
                        if (typeof item.enabled === 'undefined') item.enabled = true; // Ensure enabled state
                        addPreviewItem(paramKey, item); 
                    });
                }
            }
        });
    }

    function injectStyles() {
        const styleSheet = document.createElement("style");
        styleSheet.type = "text/css";
        styleSheet.innerText = `
            @keyframes fadeIn { from { opacity: 0; transform: scale(0.98); } to { opacity: 1; transform: scale(1); } }
            @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }

            #mj-floating-settings-button {
                position: fixed; right: 20px; bottom: 20px; padding: 10px 20px;
                background-color: #5865F2; color: white; border: none; border-radius: 8px;
                cursor: pointer; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                transition: background-color 0.2s ease, transform 0.2s ease; font-family: sans-serif; font-weight: 500;
            }
            #mj-floating-settings-button:hover { background-color: #4752C4; transform: scale(1.05); }

            .mj-toast {
                position: fixed; top: 20px; right: 20px; padding: 12px 18px; border-radius: 6px; z-index: 10001;
                font-family: sans-serif; font-size: 14px; transform: translateY(-100%); opacity: 0;
                transition: transform 0.3s ease, opacity 0.3s ease;
                background: #2B2D31; color: #DCDDDE; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            }
            .mj-toast.show { transform: translateY(0); opacity: 1; }

            #mj-control-panel {
                display: flex; flex-direction: column; position: fixed; right: 20px; bottom: 80px;
                width: 880px; max-width: calc(100vw - 40px); height: 85vh; max-height: 800px;
                border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); z-index: 10000;
                overflow: hidden; font-family: sans-serif;
                transform: translateY(20px) scale(0.95); opacity: 0; pointer-events: none;
                transition: transform 0.25s ease, opacity 0.25s ease;
                background: white; color: #111827; border: 1px solid #E0E0E0;
            }
            #mj-control-panel.visible { transform: translateY(0) scale(1); opacity: 1; pointer-events: auto; }

            .panel-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; border-bottom: 1px solid #E0E0E0; flex-shrink: 0; }
            .panel-title { margin:0; font-size:18px; font-weight:600; }
            .theme-trigger-btn { padding: 6px 12px; border-radius: 6px; border: 1px solid #D1D5DB; background-color: white; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease; }
            .theme-trigger-btn:hover { border-color: #4f46e5; }
            .theme-options-menu { position: absolute; top: calc(100% + 5px); right: 0; background-color: white; border: 1px solid #D1D5DB; border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10; min-width: 180px; padding: 4px; }
            .theme-option-button { display: flex; align-items: center; width: 100%; padding: 8px 12px; background: none; border: none; cursor: pointer; font-size: 13px; color: #1F2937; border-radius: 4px; transition: all 0.15s ease; gap: 8px; }
            .theme-option-button:hover { background-color: #f0f0f0; }
            .theme-option-button.active { background-color: #eef2ff; color: #4338ca; font-weight: 500; }

            .panel-main-content { display: flex; flex-direction: column; flex-grow: 1; overflow: hidden; }
            .panel-tabs { display: flex; padding: 12px 24px 0; border-bottom: 1px solid #E0E0E0; flex-shrink: 0; gap: 16px; }
            .tab-link { padding: 8px 4px; margin-bottom: -1px; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; font-size: 15px; color: #6B7280; transition: all 0.2s ease; }
            .tab-link:hover { color: #374151; }
            .tab-link.active { color: #4f46e5; border-bottom-color: #4f46e5; font-weight: 500; }

            .tab-content { display: none; padding: 20px 24px; overflow-y: auto; flex-grow: 1; }
            .tab-content.active { display: block; animation: fadeIn 0.3s ease; }
            .form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px 24px; }
            .form-group { display: flex; flex-direction: column; gap: 8px; }
            .form-group.span-2 { grid-column: span 2; }
            .form-group label { font-weight: 500; font-size: 14px; color: #1F2937; }

            /* Unified Input Styles START */
            .form-group input, .form-group textarea, .form-group select,
            .ref-url-input, .ref-weight-input, .weight-edit-input {
                background: white;
                color: #111827;
                border: 1px solid #D1D5DB;
                padding: 8px 12px;
                border-radius: 6px;
                font-size: 14px;
                transition: all 0.2s ease;
                box-sizing: border-box;
            }
            .form-group input:focus, .form-group textarea:focus, .form-group select:focus,
            .ref-url-input:focus, .ref-weight-input:focus, .weight-edit-input:focus {
                border-color: #4f46e5;
                box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2);
                outline: none;
            }
            /* Unified Input Styles END */

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

            .ref-grid-main { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; padding: 0; }
            .ref-module { background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
            .ref-module-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 8px; border-bottom: 1px solid #e5e7eb; }
            .ref-module-header h4 { margin: 0; font-size: 16px; font-weight: 600; color: #1f2937; }
            .global-weight-control { display: flex; align-items: center; gap: 8px; }
            .global-weight-control label { font-size: 12px; color: #6b7280; white-space: nowrap; }
            .weight-slider-mini { display: flex; align-items: center; gap: 6px; }
            .weight-slider-mini input[type="range"] { width: 60px; height: 4px; margin:0; padding:0; } 
            .weight-slider-mini span { font-size: 12px; color: #4B5563; min-width: 25px; text-align: center; font-weight: 500; }
            .ref-input-section { display: flex; gap: 8px; align-items: center; }
            .ref-url-input { flex: 1; } 
            .ref-weight-input { width: 70px; text-align: center; } 
            .ref-add-btn { padding: 8px 16px; background: #4f46e5; color: white; border: none; border-radius: 6px; font-size: 13px; cursor: pointer; transition: all 0.2s ease; white-space: nowrap; }
            .ref-add-btn:hover { background: #4338ca; }

            .ref-container-large {
                position: relative; min-height: 90px; padding: 12px;
                background: white; border-radius: 8px; border: 2px dashed #d1d5db;
                display: flex; flex-wrap: wrap; gap: 12px; align-content: flex-start; transition: all 0.2s ease;
            }
            .ref-container-large.drag-over { border-style: solid; border-color: #4f46e5; background-color: #eef2ff; }
            .ref-container-large:empty:before {
                content: '拖拽图片到此处或使用上方输入框添加'; position: absolute; top: 50%; left: 50%;
                transform: translate(-50%, -50%); color: #9ca3af; font-size: 14px; pointer-events: none; text-align: center;
            }
            .drop-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(79, 70, 229, 0.1); border-radius: inherit; align-items: center; justify-content: center; z-index: 10; }
            .drop-overlay-content { display: flex; flex-direction: column; align-items: center; gap: 8px; }
            .drop-icon { color: #4f46e5; } .drop-icon svg { display: block; }
            .drop-text { font-size: 14px; color: #4f46e5; font-weight: 500; }

            .ref-item-large {
                position: relative; width: 80px; height: 80px;
                border-radius: 8px; background: #f3f4f6; border: 2px solid #d1d5db;
                display: flex; align-items: center; justify-content: center;
                transition: all 0.2s ease; flex-shrink: 0; overflow: hidden; 
            }
            .ref-item-large:hover { transform: scale(1.05); box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-color: #4f46e5; overflow: visible; }
            .ref-item-large.disabled { opacity: 0.5; filter: grayscale(80%); }
            .ref-item-large.load-error { border-color: #ef4444; }
            .ref-image-large { width: 100%; height: 100%; object-fit: cover; }
            .ref-code-large { font-size: 11px; font-family: monospace; color: #4f46e5; text-align: center; word-break: break-all; padding: 6px; font-weight: 600; }
            .ref-error-large { font-size: 12px; color: #ef4444; text-align: center; padding: 4px; width:100%; user-select: none; }
            .weight-large {
                position: absolute; bottom: 3px; left: 3px; background: rgba(0,0,0,0.75); color: white;
                font-size: 10px; padding: 1px 4px; border-radius: 3px; font-family: monospace; font-weight: 600; z-index: 1;
            }
            .ref-edit-large, .ref-toggle-large, .ref-delete-large {
                width: 20px; height: 20px; border: none; border-radius: 4px;
                font-size: 12px; line-height: 1; cursor: pointer;
                transition: opacity 0.15s ease, background-color 0.15s ease, color 0.15s ease;
                display: flex; align-items: center; justify-content: center;
                position: absolute; opacity: 0; background: rgba(255,255,255,0.8);
                box-shadow: 0 1px 2px rgba(0,0,0,0.15); z-index: 2;
            }
            .ref-item-large:hover .ref-edit-large, .ref-item-large:hover .ref-toggle-large, .ref-item-large:hover .ref-delete-large { opacity: 1; }
            .ref-edit-large { top: 3px; left: 3px; color: #4f46e5; } .ref-edit-large svg { width:12px; height:12px; }
            .ref-edit-large:hover { background: #4f46e5; color: white; }
            .ref-delete-large { top: 3px; right: 3px; color: #ef4444; font-size: 14px; }
            .ref-delete-large:hover { background: #ef4444; color: white; }
            .ref-toggle-large { bottom: 3px; right: 3px; color: #6b7280; font-size: 14px; }
            .ref-toggle-large.active { background: #10b981; color: white; }
            .ref-toggle-large:not(.active):hover { background: #e0e0e0; }

            .weight-edit-dialog { position: fixed; top:0;left:0;right:0;bottom:0; z-index:10002; display:flex;align-items:center;justify-content:center; }
            .weight-edit-overlay { position:absolute;top:0;left:0;right:0;bottom:0; background:rgba(0,0,0,0.5); }
            .weight-edit-content { position:relative; background:white; border-radius:12px; padding:24px; min-width:300px; box-shadow:0 8px 24px rgba(0,0,0,0.15); z-index:1; }
            .weight-edit-content h4 { margin:0 0 16px 0; font-size:16px; font-weight:600; color:#1f2937; }
            .weight-edit-input-group { margin-bottom:20px; }
            .weight-edit-input-group label { display:block; margin-bottom:8px; font-size:14px; font-weight:500; color:#1f2937; }
            .weight-edit-buttons { display:flex; gap:12px; justify-content:flex-end; }
            .weight-edit-cancel, .weight-edit-save { padding:8px 16px; border-radius:6px; font-size:14px; font-weight:500; cursor:pointer; transition:all 0.2s ease; border:1px solid transparent; }
            .weight-edit-cancel { background:#e5e7eb; color:#374151; border-color:#d1d5db; } .weight-edit-cancel:hover { background:#d1d5db; }
            .weight-edit-save { background:#4f46e5; color:white; } .weight-edit-save:hover { background:#4338ca; }

            .slider-control { display: flex; align-items: center; gap: 12px; }
            .slider-control input[type="range"] { flex-grow: 1; margin:0; padding:0; height: 16px; }
            .slider-control span { font-size: 14px; color: #4B5563; min-width: 35px; text-align: right; }
            input[type="range"] { -webkit-appearance: none; background: transparent; cursor: pointer; width: 100%; }
            input[type="range"]::-webkit-slider-runnable-track { background: #E5E7EB; height: 6px; border-radius: 3px; }
            input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; margin-top: -5px; background-color: #4f46e5; height: 16px; width: 16px; border-radius: 50%; border: 2px solid white; box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
            .btn-group { display: flex; border-radius:6px; overflow:hidden; border:1px solid #d1d5db; }
            .btn-group button { flex:1; padding: 8px 10px; background:white; border:none; cursor:pointer; color: #374151; transition: all 0.2s; font-size: 13px; }
            .btn-group button:not(:last-child) { border-right: 1px solid #d1d5db; }
            .btn-group button.active { background: #4f46e5; color: white; }
            .toggle-group { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px; border-radius: 6px; background-color: #F9FAFB; }
            .toggle-group label { font-size: 14px; cursor: pointer; user-select: none; }
            .toggle-switch { position:relative; width:40px; height:20px; border-radius:10px; background:#e5e7eb; cursor:pointer; transition: background-color 0.2s ease; flex-shrink: 0; }
            .toggle-switch .toggle-dot { position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:white; box-shadow:0 1px 3px rgba(0,0,0,0.2); transition:all 0.2s ease; }
            .toggle-switch.active { background:#4f46e5; } .toggle-switch.active .toggle-dot { transform: translateX(20px); }
            #ar-section .ar-controls { display: flex; gap: 20px; align-items: center; }
            .ar-preview-container { position:relative; width:100px; height:100px; flex-shrink: 0; }
            #ratio-preview-bg { width:100px; height:100px; border:2px dashed #d1d5db; border-radius:12px; }
            #ratio-preview { position:absolute; top:0; left:0; width:100%; height:100%; display: flex; align-items: center; justify-content: center; }
            #ratio-box { background:#f3f4f6; border:2px solid #374151; border-radius:6px; display:flex; align-items:center; justify-content:center; font-size:12px; color:#374151; transition: all 0.2s ease; }
            .ar-slider-group { flex-grow: 1; display: flex; flex-direction: column; gap: 12px; }
            .ar-slider-group #size-buttons { width: 100%; }
            .panel-footer { padding: 16px 24px; border-top: 1px solid #E0E0E0; flex-shrink: 0; background-color: #f9fafb; }
            .panel-footer #prompt-params { width: 100%; height: 60px; resize: vertical; box-sizing: border-box; font-family: monospace; }
            .footer-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; }
            .footer-actions .imagine-toggle { padding: 0; background: none; }
            .footer-buttons { display: flex; gap: 12px; }
            .action-button-primary, .action-button-secondary { padding: 8px 20px; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; border: 1px solid transparent; }
            .action-button-primary { background: #4f46e5; color: white; } .action-button-primary:hover { background: #4338CA; }
            .action-button-secondary { background: #E5E7EB; color: #374151; border-color: #D1D5DB; } .action-button-secondary:hover { background: #D1D5DB; }

            /* Dark Mode Styles */
            #mj-control-panel.dark-mode { background:#2B2D31; color:#DCDDDE; border-color:#202225; }
            .dark-mode .panel-header { border-bottom-color:#202225; }
            .dark-mode .theme-trigger-btn { border-color:#2D2F34; background-color:#2B2D31; color:#DCDDDE; } .dark-mode .theme-trigger-btn:hover { border-color:#7289DA; }
            .dark-mode .theme-options-menu { background-color:#2B2D31; border-color:#202225; }
            .dark-mode .theme-option-button { color:#DCDDDE; } .dark-mode .theme-option-button:hover { background-color:#393c43; }
            .dark-mode .theme-option-button.active { background-color:#404EED; color:white; }
            .dark-mode .panel-tabs { border-bottom-color:#202225; }
            .dark-mode .tab-link { color:#8e9297; } .dark-mode .tab-link:hover { color:#dcddde; }
            .dark-mode .tab-link.active { color:#7289DA; border-bottom-color:#7289DA; }
            .dark-mode .form-group label { color:#DCDDDE; }

            .dark-mode .form-group input, .dark-mode .form-group textarea, .dark-mode .form-group select,
            .dark-mode .ref-url-input, .dark-mode .ref-weight-input, .dark-mode .weight-edit-input,
            .dark-mode .panel-footer #prompt-params {
                background:#202225;
                color:#DCDDDE;
                border-color:#40444B;
            }
            .dark-mode .form-group input:focus, .dark-mode .form-group textarea:focus, .dark-mode .form-group select:focus,
            .dark-mode .ref-url-input:focus, .dark-mode .ref-weight-input:focus, .dark-mode .weight-edit-input:focus,
            .dark-mode .panel-footer #prompt-params:focus {
                border-color:#7289DA;
                box-shadow:0 0 0 2px rgba(114,137,218,0.2);
            }
            
            .dark-mode input[type="range"]::-webkit-slider-runnable-track { background:#40444B; }
            .dark-mode input[type="range"]::-webkit-slider-thumb { background-color:#7289DA; border-color:#2B2D31; }
            .dark-mode .slider-control span { color:#b9bbbe; }
            .dark-mode .btn-group { border-color:#2D2F34; }
            .dark-mode .btn-group button { background:#40444B; color:#DCDDDE; } .dark-mode .btn-group button:not(:last-child) { border-right-color:#2D2F34; }
            .dark-mode .btn-group button.active { background:#5865F2; color:white; }
            .dark-mode .toggle-group { background-color:#202225; }
            .dark-mode .toggle-switch { background:#4E4F52; } .dark-mode .toggle-switch .toggle-dot { background:#B9BBBE; }
            .dark-mode .toggle-switch.active { background:#5865F2; }
            .dark-mode #ratio-preview-bg { border-color:#40444B; }
            .dark-mode #ratio-box { background:#40444B; color:#DCDDDE; border-color:#70747A; }
            .dark-mode .panel-footer { background-color:#2B2D31; border-top-color:#202225; }
            .dark-mode .action-button-primary { background:#5865F2; } .dark-mode .action-button-primary:hover { background:#4752C4; }
            .dark-mode .action-button-secondary { background:#40444B; color:#DCDDDE; border-color:#2D2F34; } .dark-mode .action-button-secondary:hover { background:#4F545C; }
            
            .dark-mode .ref-module { background:#1a1d21; border-color:#2D2F34; }
            .dark-mode .ref-module-header { border-bottom-color:#2D2F34; } .dark-mode .ref-module-header h4 { color:#dcddde; }
            .dark-mode .global-weight-control label { color:#8e9297; } .dark-mode .weight-slider-mini span { color:#b9bbbe; }
            .dark-mode .ref-add-btn { background:#5865F2; } .dark-mode .ref-add-btn:hover { background:#4752C4; }
            .dark-mode .ref-container-large { background:#202225; border-color:#2D2F34; }
            .dark-mode .ref-container-large:empty:before { color:#8e9297; }
            .dark-mode .ref-container-large.drag-over { border-color:#5865F2; background-color:#2f3136; }
            .dark-mode .ref-item-large { background:#313338; border-color:#2D2F34; }
            .dark-mode .ref-item-large:hover { border-color:#7289DA; }
            .dark-mode .ref-code-large { color:#7289DA; } .dark-mode .ref-error-large { color:#ff6b6b; }
            .dark-mode .weight-large { background:rgba(0,0,0,0.85); }
            .dark-mode .ref-edit-large, .dark-mode .ref-toggle-large, .dark-mode .ref-delete-large { background:rgba(79,84,92,0.8); }
            .dark-mode .ref-edit-large { color:#7289DA; } .dark-mode .ref-edit-large:hover { background:#7289DA; color:white; }
            .dark-mode .ref-delete-large { color:#ff6b6b; } .dark-mode .ref-delete-large:hover { background:#ff6b6b; color:white; }
            .dark-mode .ref-toggle-large { color:#b9bbbe; } .dark-mode .ref-toggle-large.active { background:#248046; }
            .dark-mode .ref-toggle-large:not(.active):hover { background:#555c66; }
            .dark-mode .weight-edit-content { background:#2B2D31; color:#dcddde; } .dark-mode .weight-edit-content h4 { color:#dcddde; }
            .dark-mode .weight-edit-input-group label { color:#dcddde; }
            .dark-mode .weight-edit-cancel { background:#40444B; color:#dcddde; border-color:#2D2F34; } .dark-mode .weight-edit-cancel:hover { background:#4F545C; }
            .dark-mode .weight-edit-save { background:#5865F2; } .dark-mode .weight-edit-save:hover { background:#4752C4; }
        `;
        document.head.appendChild(styleSheet);
    }

    function init() {
        injectStyles();
        resetParams(); 
        createSettingButton();
        createControlPanel(); 
        updateAllUIElements(); // Initialize UI elements based on default params
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        setTimeout(init, 500); // Delay init slightly to ensure Discord UI is fully loaded
    } else {
        window.addEventListener('DOMContentLoaded', () => setTimeout(init, 500), { once: true });
    }
})();