Discord Midjourney 参数面板

在 Discord 中添加 Midjourney 参数设置面板,支持完整卡片式 UI 和最新参数功能(⚠️⚠️⚠️需开启开发者模式)

目前為 2025-05-13 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Discord Midjourney 参数面板
// @namespace    https://github.com/cwser
// @version      0.1.5
// @license      MIT
// @description  在 Discord 中添加 Midjourney 参数设置面板,支持完整卡片式 UI 和最新参数功能(⚠️⚠️⚠️需开启开发者模式)
// @author       cwser
// @match        https://discord.com/*
// @icon         https://www.midjourney.com/favicon.ico
// @grant        unsafeWindow
// @supportURL   https://github.com/cwser
// @homepageURL  https://github.com/cwser
// ==/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, weight}
        sref: [],    // 格式: {url, weight}
        oref: [],    // 格式: {url, weight}
        iref: [],
        directImages: [], // 格式: {url, weight}
        // 新增参数
        tile: false,
        seed: '',
        quality: 1,
        stop: 100,
        visibility: '',
        // 新增个性化参数
        personalParams: ''
    };

    // 显示 Toast 提示
    function showToast(message) {
        const toast = document.createElement('div');
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(0,0,0,0.8);
            color: white;
            padding: 10px 16px;
            border-radius: 6px;
            z-index: 99999;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            transform: translateY(-20px);
            opacity: 0;
            transition: all 0.3s ease;
        `;

        document.body.appendChild(toast);

        // 触发动画
        setTimeout(() => {
            toast.style.transform = 'translateY(0)';
            toast.style.opacity = '1';
        }, 10);

        // 自动消失
        setTimeout(() => {
            toast.style.transform = 'translateY(-20px)';
            toast.style.opacity = '0';
            setTimeout(() => document.body.removeChild(toast), 300);
        }, 2000);
    }

    // 创建设置按钮
    function createSettingButton() {
        const button = document.createElement('button');
        button.textContent = 'MJ设置';
        button.style.cssText = `
            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: all 0.2s ease;
        `;

        button.addEventListener('click', toggleControlPanel);
        document.body.appendChild(button);

        // 添加悬停效果
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#4752C4';
            button.style.transform = 'scale(1.05)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#5865F2';
            button.style.transform = 'scale(1)';
        });
    }

    // 切换控制面板显示/隐藏
    function toggleControlPanel() {
        const panel = document.getElementById('mj-control-panel');
        if (panel) {
            if (panel.style.display === 'none') {
                panel.style.display = 'block';
                // 添加显示动画
                panel.style.opacity = '0';
                panel.style.transform = 'translateY(10px)';
                setTimeout(() => {
                    panel.style.opacity = '1';
                    panel.style.transform = 'translateY(0)';
                }, 10);
            } else {
                // 添加隐藏动画
                panel.style.opacity = '0';
                panel.style.transform = 'translateY(10px)';
                setTimeout(() => {
                    panel.style.display = 'none';
                    panel.style.opacity = '1';
                    panel.style.transform = 'translateY(0)';
                }, 200);
            }
        }
    }

    // 重置参数
    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: [],
            iref: [],
            directImages: [],
            // 重置新增参数
            tile: false,
            seed: '',
            quality: 1,
            stop: 100,
            visibility: '',
            // 新增个性化参数
            personalParams: ''
        };
    }

    // 更新提示词参数
    function updatePromptParams() {
        const { prompt, ar, stylize, weird, chaos, mode, draft, noPrompt, version, cref, sref, speed, oref, iref, directImages, tile, seed, quality, stop, visibility, personalParams } = params;

        // 处理其他参数
        const otherParts = [
            `--ar ${ar}`,
            stylize !== 0 ? `--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}`,
            // 新增参数处理
            tile ? '--tile' : '',
            seed ? `--seed ${seed}` : '',
            quality !== 1 ? `--q ${quality}` : '',
            stop !== 100 ? `--stop ${stop}` : '',
            visibility ? `--${visibility}` : '',
            // 新增个性化参数处理
            personalParams ? `--p ${personalParams}` : ''
        ];

        // 处理带权重的图像参考
        const formatImageWithWeight = (url, weight, prefix) => {
            return weight ? `${url} ${prefix} ${weight}` : url;
        };

        // 直接图像参考不使用参数标识,直接添加到提示词中
        const directImageUrls = directImages.map(item => formatImageWithWeight(item.url, item.weight, '--iw')).join(' ');

        const promptField = document.getElementById('prompt-params');
        if (promptField) {
            // 构建完整提示词:图像参考URL + 主提示词 + --cref/--sref/--oref + 其他参数
            const allParts = [
                directImageUrls,
                prompt.trim(),
                ...cref.map(item => `--cref ${formatImageWithWeight(item.url, item.weight, '--cw')}`),
                ...sref.map(item => `--sref ${formatImageWithWeight(item.url, item.weight, '--sw')}`),
                ...oref.map(item => `--oref ${formatImageWithWeight(item.url, item.weight, '--ow')}`),
                ...otherParts.filter(Boolean)
            ].filter(Boolean);

            promptField.value = allParts.join(' ').trim();
        }
    }

    // 创建控制面板
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.id = 'mj-control-panel';
        panel.style.cssText = `
            display: none;
            position: fixed;
            right: 20px;
            bottom: 80px;
            width: 1080px;
            max-width: calc(100% - 40px);
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
            padding: 20px;
            z-index: 10000;
            border: 1px solid #E5E7EB;
            max-height: 90vh;
            overflow-y: auto;
            font-family: sans-serif;
            transition: all 0.2s ease;
            color: #111827;
        `;

        panel.innerHTML = `
            <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:20px;">
                <h3 style="margin:0; color:#111827; font-size:18px; font-weight:600;">Midjourney 参数设置</h3>
                <div style="flex:1; height:1px; background:#e5e7eb; margin:0 16px;"></div>
                <button id="theme-toggle" style="background:none; border:none; cursor:pointer; color:#6B7280; transition: color 0.2s ease;">
                    <i class="fa-regular fa-moon"></i>
                </button>
            </div>

            <div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap:20px;">
                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">图片尺寸</p>
                    <div style="display:flex; align-items:center; gap:20px;">
                        <div style="position:relative; width:100px; height:100px;">
                            <div style="position:absolute; top:0; left:0; width:100px; height:100px; border:2px dashed #d1d5db; border-radius:12px;"></div>
                            <div id="ratio-preview" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:100px; height:100px;">
                                <div style="width:100px; height:100px; border:2px dashed #d1d5db; border-radius:12px; position:absolute; top:0; left:0;"></div>
                                <div id="ratio-box" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); background:#f3f4f6; border:2px solid #374151; border-radius:6px; display:flex; align-items:center; justify-content:center; font-size:12px; color:#374151; padding:2px 4px; min-width:20px; min-height:12px; box-sizing: border-box;">1:1</div>
                            </div>
                        </div>
                        <div style="flex:1;">
                            <div id="size-buttons" style="display:flex; gap:8px; margin-bottom:8px;"></div>
                            <input type="range" id="ratio-slider" min="0" max="10" value="5" style="width:100%;">
                        </div>
                    </div>
                </div>

                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">美学参数</p>
                    <div style="display:flex; flex-direction:column; gap:12px;">
                        <div style="display:flex; align-items:center; justify-content:space-between;">
                            <label style="flex:1; margin-right:12px;">风格化</label>
                            <input type="range" id="stylize" min="0" max="1000" value="100" style="flex:3;">
                            <span id="stylize-value" style="width:40px; text-align:right;">100</span>
                        </div>
                        <div style="display:flex; align-items:center; justify-content:space-between;">
                            <label style="flex:1; margin-right:12px;">奇特化</label>
                            <input type="range" id="weird" min="0" max="3000" value="0" style="flex:3;">
                            <span id="weird-value" style="width:40px; text-align:right;">0</span>
                        </div>
                        <div style="display:flex; align-items:center; justify-content:space-between;">
                            <label style="flex:1; margin-right:12px;">多样性</label>
                            <input type="range" id="chaos" min="0" max="100" value="0" style="flex:3;">
                            <span id="chaos-value" style="width:40px; text-align:right;">0</span>
                        </div>
                    </div>
                </div>

                <div style="background:#f9fafb; padding:16px; border-radius:8px; display: flex; flex-direction: column; gap: 10px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">模型设置</p>
                    <div style="display:flex; gap:10px; align-items:center;">
                        <label style="flex:1;">速度
                            <div class="btn-group" style="display:flex; border-radius:6px; overflow:hidden; border:1px solid #d1d5db;">
                                <button data-value="relax" class="speed-btn active" style="flex:1; padding:6px; background:white; border:none; cursor:pointer;">标准</button>
                                <button data-value="fast" class="speed-btn" style="flex:1; padding:6px; background:white; border:none; cursor:pointer;">快速</button>
                                <button data-value="turbo" class="speed-btn" style="flex:1; padding:6px; background:white; border:none; cursor:pointer;">极速</button>
                            </div>
                        </label>
                        <label style="flex:1;">模式
                            <div class="btn-group" style="display:flex; border-radius:6px; overflow:hidden; border:1px solid #d1d5db;">
                                <button data-value="standard" class="mode-btn active" style="flex:1; padding:6px; background:white; border:none; cursor:pointer;">标准</button>
                                <button data-value="raw" class="mode-btn" style="flex:1; padding:6px; background:white; border:none; cursor:pointer;">原始</button>
                            </div>
                        </label>
                    </div>
                    <div style="display:flex; gap:10px; align-items:center;">
                        <label style="flex:1;">草稿
                            <div class="toggle-switch" style="position:relative; width:40px; height:20px; border-radius:10px; background:#e5e7eb; cursor:pointer;">
                                <div class="toggle-dot" style="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;"></div>
                            </div>
                        </label>
                        <label style="flex:2;">版本
                            <select id="version-select" style="width:100%; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                                <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>
                        </label>
                    </div>
                </div>

                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">角色参考 (--cref)</p>
                    <div style="display:flex; gap:8px; margin-bottom:8px;">
                        <input type="text" id="cref-url" placeholder="粘贴角色图片URL" style="flex:3; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                        <input type="number" id="cref-weight" placeholder="权重" style="flex:1; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                    </div>
                    <button id="cref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer; transition:all 0.2s ease;">添加</button>
                    <div id="cref-preview" style="margin-top:10px; display:flex; flex-wrap:wrap; gap:8px;"></div>
                </div>

                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">风格参考 (--sref)</p>
                    <div style="display:flex; gap:8px; margin-bottom:8px;">
                        <input type="text" id="sref-url" placeholder="粘贴风格图片URL" style="flex:3; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                        <input type="number" id="sref-weight" placeholder="权重" style="flex:1; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                    </div>
                    <button id="sref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer; transition:all 0.2s ease;">添加</button>
                    <div id="sref-preview" style="margin-top:10px; display:flex; flex-wrap:wrap; gap:8px;"></div>
                </div>

                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">全方位参考 (--oref)</p>
                    <div style="display:flex; gap:8px; margin-bottom:8px;">
                        <input type="text" id="oref-url" placeholder="粘贴全方位参考图片URL" style="flex:3; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                        <input type="number" id="oref-weight" placeholder="权重" style="flex:1; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                    </div>
                    <button id="oref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer; transition:all 0.2s ease;">添加</button>
                    <div id="oref-preview" style="margin-top:10px; display:flex; flex-wrap:wrap; gap:8px;"></div>
                </div>

                <!-- 图像参考板块 -->
                <div style="background:#f9fafb; padding:16px; border-radius:8px;">
                    <p style="margin:0 0 8px 0; font-weight:500;">图像参考</p>
                    <div style="display:flex; gap:8px; margin-bottom:8px;">
                        <input type="text" id="direct-image-url" placeholder="粘贴图像URL" style="flex:3; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                        <input type="number" id="direct-image-weight" placeholder="权重" style="flex:1; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                    </div>
                    <button id="direct-image-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer; transition:all 0.2s ease;">添加</button>
                    <div id="direct-image-preview" style="margin-top:10px; display:flex; flex-wrap:wrap; gap:8px;"></div>
                </div>

                <!-- 新增:更多参数板块 -->
                <div style="background:#f9fafb; padding:16px; border-radius:8px; grid-column: span 2;">
                    <p style="margin:0 0 8px 0; font-weight:500;">更多参数</p>
                    <div style="display:grid; grid-template-columns: 1fr 1fr; gap:16px;">
                        <div>
                            <div style="display:flex; align-items:center; gap:8px; margin-bottom:16px;">
                                <div class="toggle-switch" id="tile-toggle-switch" style="position:relative; width:40px; height:20px; border-radius:10px; background:#e5e7eb; cursor:pointer;">
                                    <div class="toggle-dot" style="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;"></div>
                                </div>
                                <label>重复图案 --tile</label>
                            </div>
                            <div style="display:flex; align-items:center; gap:8px; margin-bottom:16px;">
                                <label>种子 --seed</label>
                                <input type="number" id="seed-input" placeholder="0-4294967295" min="0" max="4294967295" style="width:120px; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                            </div>
                            <!-- 新增:个性化参数 -->
                            <div style="display:flex; align-items:center; gap:8px; margin-bottom:16px;">
                                <label>个性化 --p</label>
                                <input type="text" id="personal-params" placeholder="输入个性化参数" style="flex:1; padding:6px; border-radius:6px; border:1px solid #d1d5db;">
                            </div>
                        </div>
                        <div>
                            <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:16px;">
                                <label>质量 --q</label>
                                <input type="range" id="quality-slider" min="0" max="4" step="0.25" value="1" style="flex:1; margin:0 10px;">
                                <span id="quality-value">1</span>
                            </div>
                            <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:16px;">
                                <label>停止 --stop</label>
                                <input type="range" id="stop-slider" min="10" max="100" step="1" value="100" style="flex:1; margin:0 10px;">
                                <span id="stop-value">100</span>
                            </div>
                        </div>
                    </div>
                    <div style="margin-top:10px;">
                        <p style="margin:0 0 8px 0; font-weight:500;">可见性</p>
                        <div style="display:flex; gap:10px;">
                            <button id="stealth-btn" class="visibility-btn active" style="padding:6px 12px; border:1px solid #d1d5db; border-radius:6px; cursor:pointer; transition:all 0.2s ease;">隐身</button>
                            <button id="public-btn" class="visibility-btn" style="padding:6px 12px; border:1px solid #d1d5db; border-radius:6px; cursor:pointer; transition:all 0.2s ease;">公开</button>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 修改:将三个输入框(提示词、排除词、最终参数)放在同一排 -->
            <div style="margin-top:20px; border-top:1px solid #e5e7eb; padding-top:16px;">
                <div style="display:flex; gap:10px;">
                    <div style="flex:1;">
                        <p style="margin:0 0 8px 0; font-weight:500;">提示词</p>
                        <textarea id="main-prompt" placeholder="输入主要提示词..." style="width:100%; height:80px; padding:10px; box-sizing:border-box; border-radius:6px; border:1px solid #d1d5db; transition:border-color 0.2s ease;"></textarea>
                    </div>
                    <div style="flex:1;">
                        <p style="margin:0 0 8px 0; font-weight:500;">排除词</p>
                        <textarea id="no-prompt" placeholder="输入需要排除的元素,多个用空格分隔" style="width:100%; height:80px; padding:10px; box-sizing:border-box; border-radius:6px; border:1px solid #d1d5db; transition:border-color 0.2s ease;"></textarea>
                    </div>
                    <div style="flex:1;">
                        <p style="margin:0 0 8px 0; font-weight:500;">最终参数</p>
                        <textarea id="prompt-params" style="width:100%; height:80px; padding:10px; background:#f9fafb; border-radius:6px; border:1px solid #d1d5db; transition:border-color 0.2s ease; cursor:pointer;" readonly></textarea>
                    </div>
                </div>
            </div>

            <div style="margin-top:16px; display:flex; gap:10px;">
                <button id="copy-btn" style="flex:1; padding:8px; background:#4f46e5; color:white; border:none; border-radius:6px; cursor:pointer; transition:all 0.2s ease;">拷贝参数</button>
                <button id="clear-btn" style="flex:1; padding:8px; background:#e5e7eb; color:#111827; border:none; border-radius:6px; cursor:pointer; transition:all 0.2s ease;">清空参数</button>
            </div>
        `;

        document.body.appendChild(panel);
        bindControlEvents();
    }

function setInitialActiveButtons() {
    const buttonGroups = [
        { className: 'speed-btn', param: 'speed' },
        { className: 'mode-btn', param: 'mode' },
        { className: 'visibility-btn', param: 'visibility' }
    ];

    buttonGroups.forEach(group => {
        const buttons = document.querySelectorAll(`.${group.className}`);
        buttons.forEach(btn => {
            if (btn.dataset.value === params[group.param]) {
                btn.classList.add('active');
                btn.style.backgroundColor = '#4f46e5';
                btn.style.color = 'white';
            } else {
                btn.classList.remove('active');
                btn.style.backgroundColor = 'white';
                btn.style.color = '#111827';
            }
        });
    });
}

    // 绑定控制面板事件
    function bindControlEvents() {
        const $ = id => document.getElementById(id);

        // 切换主题
        let isDarkMode = false;
        $('theme-toggle').addEventListener('click', () => {
            isDarkMode = !isDarkMode;
            const panel = $('mj-control-panel');
            const buttons = document.querySelectorAll('button:not(.visibility-btn):not(.speed-btn):not(.mode-btn)');
            const inputs = document.querySelectorAll('input, textarea');
            const sliders = document.querySelectorAll('input[type="range"]');
            const toggleSwitches = document.querySelectorAll('.toggle-switch');
            const bgElements = document.querySelectorAll('.btn-group, [style*="background:#f9fafb"]');

            if (isDarkMode) {
                panel.style.backgroundColor = '#2B2D31';
                panel.style.color = '#E4E6EB';
                panel.style.borderColor = '#4E4F52';

                buttons.forEach(btn => {
                    btn.style.backgroundColor = '#4E4F52';
                    btn.style.color = '#E4E6EB';
                });

                inputs.forEach(input => {
                    input.style.backgroundColor = '#1E1F22';
                    input.style.color = '#E4E6EB';
                    input.style.borderColor = '#4E4F52';
                });

                sliders.forEach(slider => {
                    slider.style.filter = 'brightness(0.7)';
                });

                toggleSwitches.forEach(switchEl => {
                    switchEl.style.backgroundColor = '#4E4F52';
                });

                bgElements.forEach(el => {
                    el.style.backgroundColor = '#1E1F22';
                });

                $('theme-toggle').innerHTML = '<i class="fa-regular fa-sun"></i>';
            } else {
                panel.style.backgroundColor = 'white';
                panel.style.color = '#111827';
                panel.style.borderColor = '#E5E7EB';

                buttons.forEach(btn => {
                    btn.style.backgroundColor = '';
                    btn.style.color = '';
                });

                inputs.forEach(input => {
                    input.style.backgroundColor = '';
                    input.style.color = '';
                    input.style.borderColor = '#d1d5db';
                });

                sliders.forEach(slider => {
                    slider.style.filter = '';
                });

                toggleSwitches.forEach(switchEl => {
                    switchEl.style.backgroundColor = '#e5e7eb';
                });

                bgElements.forEach(el => {
                    el.style.backgroundColor = '#f9fafb';
                });

                $('theme-toggle').innerHTML = '<i class="fa-regular fa-moon"></i>';
            }
        });

        // 绑定提示词输入框事件
        $('main-prompt').oninput = e => {
            params.prompt = e.target.value;
            updatePromptParams();
        };

        // 绑定滑块事件
        const bindSliderEvent = (id, property, displayId = null) => {
            $(id).oninput = e => {
                const value = e.target.type === 'range' ? +e.target.value : e.target.value;
                params[property] = value;
                if (displayId) $(displayId).textContent = value;
                updatePromptParams();
            };
        };

        bindSliderEvent('stylize', 'stylize', 'stylize-value');
        bindSliderEvent('weird', 'weird', 'weird-value');
        bindSliderEvent('chaos', 'chaos', 'chaos-value');
        bindSliderEvent('quality-slider', 'quality', 'quality-value');
        bindSliderEvent('stop-slider', 'stop', 'stop-value');

        // 绑定单选按钮组
        const bindRadioGroup = (groupClass, property) => {
            document.querySelectorAll(`.${groupClass}`).forEach(btn => {
                btn.addEventListener('click', () => {
                    // 移除同组其他按钮的active状态
                    document.querySelectorAll(`.${groupClass}`).forEach(b => {
                        b.classList.remove('active');
                        b.style.backgroundColor = 'white';
                        b.style.color = '#111827';
                    });

                    // 添加当前按钮的active状态
                    btn.classList.add('active');
                    btn.style.backgroundColor = '#4f46e5';
                    btn.style.color = 'white';

                    // 更新参数
                    params[property] = btn.dataset.value || btn.id.replace('-btn', '');
                    updatePromptParams();
                });
            });
        };

        bindRadioGroup('speed-btn', 'speed');
        bindRadioGroup('mode-btn', 'mode');
        bindRadioGroup('visibility-btn', 'visibility');

        // 绑定开关切换
        const bindToggleSwitch = (switchId, property) => {
            const switchEl = $(switchId);
            const dot = switchEl.querySelector('.toggle-dot');

            switchEl.addEventListener('click', () => {
                params[property] = !params[property];

                if (params[property]) {
                    dot.style.transform = 'translateX(20px)';
                    switchEl.style.backgroundColor = '#4f46e5';
                } else {
                    dot.style.transform = 'translateX(0)';
                    switchEl.style.backgroundColor = '#e5e7eb';
                }

                updatePromptParams();
            });
        };

        bindToggleSwitch('tile-toggle-switch', 'tile');

        // 绑定草稿开关
        const draftToggle = document.querySelector('.toggle-switch');
        const draftDot = draftToggle.querySelector('.toggle-dot');

        draftToggle.addEventListener('click', () => {
            params.draft = !params.draft;

            if (params.draft) {
                draftDot.style.transform = 'translateX(20px)';
                draftToggle.style.backgroundColor = '#4f46e5';
            } else {
                draftDot.style.transform = 'translateX(0)';
                draftToggle.style.backgroundColor = '#e5e7eb';
            }

            updatePromptParams();
        });

        // 绑定版本选择
        $('version-select').onchange = e => {
            params.version = e.target.value;
            updatePromptParams();
        };

        // 绑定排除词
        $('no-prompt').oninput = e => {
            params.noPrompt = e.target.value.trim();
            updatePromptParams();
        };

        // 绑定复制按钮
        $('copy-btn').onclick = () => {
            const textarea = $('prompt-params');
            textarea.select();
            document.execCommand('copy');
            showToast('参数已复制!');
        };

        // 绑定清空按钮
        $('clear-btn').onclick = () => {
            resetParams();

            // 重置所有控件为默认值
            $('main-prompt').value = '';
            $('stylize').value = 100;
            $('weird').value = 0;
            $('chaos').value = 0;
            $('version-select').value = 'v7';
            $('no-prompt').value = '';
            $('ratio-slider').value = 5;
            $('oref-url').value = '';
            $('direct-image-url').value = '';
            $('cref-weight').value = '';
            $('sref-weight').value = '';
            $('oref-weight').value = '';
            $('direct-image-weight').value = '';

            // 重置新增参数
            $('seed-input').value = '';
            $('quality-slider').value = 1;
            $('quality-value').textContent = '1';
            $('stop-slider').value = 100;
            $('stop-value').textContent = '100';
            // 重置个性化参数
            $('personal-params').value = '';

            // 重置开关状态
            draftDot.style.transform = 'translateX(0)';
            draftToggle.style.backgroundColor = '#e5e7eb';

            const tileDot = document.querySelector('#tile-toggle-switch .toggle-dot');
            tileDot.style.transform = 'translateX(0)';
            document.querySelector('#tile-toggle-switch').style.backgroundColor = '#e5e7eb';

            // 重置按钮组状态
            document.querySelectorAll('.btn-group button').forEach(btn => {
                btn.classList.remove('active');
                btn.style.backgroundColor = 'white';
                btn.style.color = '#111827';
            });

            // 设置默认选中按钮
            document.querySelector('.speed-btn[data-value="relax"]').classList.add('active');
            document.querySelector('.speed-btn[data-value="relax"]').style.backgroundColor = '#4f46e5';
            document.querySelector('.speed-btn[data-value="relax"]').style.color = 'white';

            document.querySelector('.mode-btn[data-value="standard"]').classList.add('active');
            document.querySelector('.mode-btn[data-value="standard"]').style.backgroundColor = '#4f46e5';
            document.querySelector('.mode-btn[data-value="standard"]').style.color = 'white';

            // 移除所有可见性按钮的 active 状态
            document.querySelectorAll('.visibility-btn').forEach(btn => {
                btn.classList.remove('active');
                btn.style.backgroundColor = 'white';
                btn.style.color = '#111827';
            });

            // 设置参数为空
            params.visibility = '';

            // 触发事件以更新UI
            $('main-prompt').dispatchEvent(new Event('input'));
            $('stylize').dispatchEvent(new Event('input'));
            $('weird').dispatchEvent(new Event('input'));
            $('chaos').dispatchEvent(new Event('input'));
            $('version-select').dispatchEvent(new Event('change'));
            $('no-prompt').dispatchEvent(new Event('input'));
            $('ratio-slider').dispatchEvent(new Event('input'));
            $('seed-input').dispatchEvent(new Event('input'));
            $('quality-slider').dispatchEvent(new Event('input'));
            $('stop-slider').dispatchEvent(new Event('input'));
            // 触发个性化参数事件
            $('personal-params').dispatchEvent(new Event('input'));

            // 清空输入框
            $('cref-url').value = '';
            $('sref-url').value = '';

            // 刷新预览
            refreshPreviews();

            // 显示重置成功提示
            showToast('所有参数已重置为默认值');
        };

        // 绑定角色参考事件
        $('cref-add').onclick = () => {
            const url = $('cref-url').value.trim();
            const weight = $('cref-weight').value.trim();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.cref.some(item => item.url === url)) {
                    params.cref.push({url, weight});
                    addPreviewImage('cref-preview', url, weight, 'cref', '--cw');
                    $('cref-url').value = '';
                    $('cref-weight').value = '';
                    updatePromptParams();
                } else {
                    showToast('该URL已添加');
                }
            } else {
                showToast('请输入有效图片URL');
            }
        };

        // 绑定风格参考事件
        $('sref-add').onclick = () => {
            const url = $('sref-url').value.trim();
            const weight = $('sref-weight').value.trim();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.sref.some(item => item.url === url)) {
                    params.sref.push({url, weight});
                    addPreviewImage('sref-preview', url, weight, 'sref', '--sw');
                    $('sref-url').value = '';
                    $('sref-weight').value = '';
                    updatePromptParams();
                } else {
                    showToast('该URL已添加');
                }
            } else {
                showToast('请输入有效图片URL');
            }
        };

        // 绑定全方位参考事件
        $('oref-add').onclick = () => {
            const url = $('oref-url').value.trim();
            const weight = $('oref-weight').value.trim();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.oref.some(item => item.url === url)) {
                    params.oref.push({url, weight});
                    addPreviewImage('oref-preview', url, weight, 'oref', '--ow');
                    $('oref-url').value = '';
                    $('oref-weight').value = '';
                    updatePromptParams();
                } else {
                    showToast('该URL已添加');
                }
            } else {
                showToast('请输入有效图片URL');
            }
        };

        // 绑定直接图像参考事件
        $('direct-image-add').onclick = () => {
            const url = $('direct-image-url').value.trim();
            const weight = $('direct-image-weight').value.trim();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.directImages.some(item => item.url === url)) {
                    params.directImages.push({url, weight});
                    addPreviewImage('direct-image-preview', url, weight, 'directImages', '--iw');
                    $('direct-image-url').value = '';
                    $('direct-image-weight').value = '';
                    updatePromptParams();
                } else {
                    showToast('该URL已添加');
                }
            } else {
                showToast('请输入有效图片URL');
            }
        };

        // 绑定比例滑块事件
        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': { width: 50, height: 100 },
            '9:16': { width: 56.25, height: 100 },
            '2:3': { width: 66.67, height: 100 },
            '3:4': { width: 75, height: 100 },
            '5:6': { width: 83.33, height: 100 },
            '1:1': { width: 100, height: 100 },
            '6:5': { width: 100, height: 83.33 },
            '4:3': { width: 100, height: 75 },
            '3:2': { width: 100, height: 66.67 },
            '16:9': { width: 100, height: 56.25 },
            '2:1': { width: 100, height: 50 }
        };

        $('ratio-slider').oninput = e => {
            const ratio = sizeMap[+e.target.value] || '1:1';
            params.ar = ratio;
            const box = document.getElementById('ratio-box');
            const preset = ratioPresets[ratio] || { width: 100, height: 100 };
            box.style.width = `${preset.width}px`;
            box.style.height = `${preset.height}px`;
            box.textContent = ratio;

            // 更新预设按钮状态
            const presetMap = {
                '纵向': '1:2',
                '正方形': '1:1',
                '横向': '2:1'
            };

            document.querySelectorAll('#size-buttons button').forEach(btn => {
                const btnRatio = presetMap[btn.textContent];
                if (btnRatio === ratio) {
                    btn.classList.add('active');
                    btn.style.backgroundColor = '#4f46e5';
                    btn.style.color = 'white';
                } else {
                    btn.classList.remove('active');
                    btn.style.backgroundColor = 'white';
                    btn.style.color = '#111827';
                }
            });

            updatePromptParams();
        };

        // 设置比例预设按钮
        const btnGroup = document.getElementById('size-buttons');
        const presetMap = {
            '纵向': '1:2',
            '正方形': '1:1',
            '横向': '2:1'
        };

        ['纵向', '正方形', '横向'].forEach((label, i) => {
            const btn = document.createElement('button');
            btn.textContent = label;
            btn.dataset.value = presetMap[label];
            btn.style.cssText = 'padding:4px 12px; border-radius:6px; border:1px solid #d1d5db; background:white; cursor:pointer; transition:all 0.2s ease;';

            // 初始化按钮状态
            if (i === 1) { // 默认选中正方形
                btn.classList.add('active');
                btn.style.backgroundColor = '#4f46e5';
                btn.style.color = 'white';
            }

            // 绑定按钮点击事件
            btn.onclick = () => {
                const ratio = presetMap[label];
                const sliderIndex = sizeMap.indexOf(ratio);

                if (sliderIndex !== -1) {
                    $('ratio-slider').value = sliderIndex;
                    $('ratio-slider').dispatchEvent(new Event('input'));
                }
            };

            btnGroup.appendChild(btn);
        });

        // 绑定种子输入
        $('seed-input').oninput = e => {
            const value = e.target.value.trim();
            if (/^\d*$/.test(value) && (value === '' || (parseInt(value) >= 0 && parseInt(value) <= 4294967295))) {
                params.seed = value;
                updatePromptParams();
            } else {
                e.target.value = params.seed; // 恢复之前的值
            }
        };

        // 绑定个性化参数
        $('personal-params').oninput = e => {
            params.personalParams = e.target.value.trim();
            updatePromptParams();
        };

        // 绑定参数输出框点击事件(支持一键复制)
        $('prompt-params').addEventListener('click', () => {
            const textarea = $('prompt-params');
            textarea.select();
            document.execCommand('copy');
            showToast('参数已复制!');
        });

        // 为输入框添加焦点样式
        document.querySelectorAll('input, textarea').forEach(el => {
            el.addEventListener('focus', () => {
                el.style.borderColor = '#4f46e5';
            });

            el.addEventListener('blur', () => {
                el.style.borderColor = '#d1d5db';
            });
        });

        // 为按钮添加悬停效果
        document.querySelectorAll('button').forEach(btn => {
            btn.addEventListener('mouseenter', () => {
                if (!btn.classList.contains('active')) {
                    btn.style.transform = 'translateY(-1px)';
                    btn.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
                }
            });

            btn.addEventListener('mouseleave', () => {
                btn.style.transform = 'translateY(0)';
                btn.style.boxShadow = 'none';
            });
        });
    }

    // 添加预览图片并绑定删除功能
    function addPreviewImage(containerId, url, weight, paramType, weightPrefix) {
        const container = document.getElementById(containerId);
        const imgContainer = document.createElement('div');
        imgContainer.style.cssText = 'position: relative; margin: 4px;';

        const img = document.createElement('img');
        img.src = url;
        img.style.width = '60px';
        img.style.height = '60px';
        img.style.objectFit = 'cover';
        img.style.borderRadius = '4px';
        img.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
        img.style.transition = 'all 0.2s ease';

        // 添加图片悬停效果
        img.addEventListener('mouseenter', () => {
            img.style.transform = 'scale(1.05)';
            img.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)';
        });

        img.addEventListener('mouseleave', () => {
            img.style.transform = 'scale(1)';
            img.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
        });

        // 添加权重显示
        const weightBadge = document.createElement('div');
        weightBadge.style.cssText = 'position: absolute; bottom: 0; left: 0; background: rgba(0,0,0,0.7); color: white; font-size: 10px; padding: 1px 3px; border-radius: 0 4px 0 4px;';
        weightBadge.textContent = weight ? `${weightPrefix.replace('--', '')}:${weight}` : `${weightPrefix.replace('--', '')}:默认`;

        const deleteBtn = document.createElement('button');
        deleteBtn.style.cssText = 'position: absolute; top: -4px; right: -4px; background: rgba(239, 68, 68, 0.9); color: white; border: none; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; line-height: 1; cursor:pointer; transition:all 0.2s ease; opacity: 0.8;';
        deleteBtn.textContent = '×';
        deleteBtn.onclick = function(e) {
            e.stopPropagation();

            // 添加删除动画
            img.style.opacity = '0';
            img.style.transform = 'scale(0.8)';
            imgContainer.style.transform = 'translateY(10px)';
            imgContainer.style.opacity = '0';

            setTimeout(() => {
                const index = paramType === 'cref'
                    ? params.cref.findIndex(item => item.url === url)
                    : paramType === 'sref'
                        ? params.sref.findIndex(item => item.url === url)
                        : paramType === 'oref'
                            ? params.oref.findIndex(item => item.url === url)
                            : params.directImages.findIndex(item => item.url === url);

                if (index !== -1) {
                    if (paramType === 'cref') {
                        params.cref.splice(index, 1);
                    } else if (paramType === 'sref') {
                        params.sref.splice(index, 1);
                    } else if (paramType === 'oref') {
                        params.oref.splice(index, 1);
                    } else { // directImages
                        params.directImages.splice(index, 1);
                    }
                    container.removeChild(imgContainer);
                    updatePromptParams();
                }
            }, 200);
        };

        // 添加按钮悬停效果
        deleteBtn.addEventListener('mouseenter', () => {
            deleteBtn.style.opacity = '1';
            deleteBtn.style.transform = 'scale(1.1)';
        });

        deleteBtn.addEventListener('mouseleave', () => {
            deleteBtn.style.opacity = '0.8';
            deleteBtn.style.transform = 'scale(1)';
        });

        imgContainer.appendChild(img);
        imgContainer.appendChild(weightBadge);
        imgContainer.appendChild(deleteBtn);
        container.appendChild(imgContainer);
    }

    // 刷新预览区域
    function refreshPreviews() {
        ['cref', 'sref', 'oref', 'directImages'].forEach(type => {
            const container = document.getElementById(`${type === 'directImages' ? 'direct-image' : type}-preview`);
            container.innerHTML = '';

            const weightPrefix = {
                'cref': '--cw',
                'sref': '--sw',
                'oref': '--ow',
                'directImages': '--iw'
            }[type];

            params[type].forEach(item => {
                addPreviewImage(`${type === 'directImages' ? 'direct-image' : type}-preview`, item.url, item.weight, type, weightPrefix);
            });
        });
    }

    // 初始化
    function init() {
        resetParams();
        createSettingButton();
        createControlPanel();
        setInitialActiveButtons();

        // 手动触发比例滑块的input事件以更新预览
        const ratioSlider = document.getElementById('ratio-slider');
        if (ratioSlider) ratioSlider.dispatchEvent(new Event('input'));
        updatePromptParams();
    }

    window.addEventListener('load', init);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址