Greasy Fork

Greasy Fork is available in English.

Discord Midjourney 参数面板

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Discord Midjourney 参数面板
// @namespace    https://github.com/cwser
// @version      0.1.0
// @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}
    };

    function createSettingButton() {
        const button = document.createElement('button');
        button.textContent = 'MJ设置';
        button.style.position = 'fixed';
        button.style.right = '20px';
        button.style.bottom = '20px';
        button.style.padding = '10px 20px';
        button.style.backgroundColor = '#5865F2';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '8px';
        button.style.cursor = 'pointer';
        button.style.zIndex = '9999';
        button.addEventListener('click', toggleControlPanel);
        document.body.appendChild(button);
    }

    function toggleControlPanel() {
        const panel = document.getElementById('mj-control-panel');
        if (panel) {
            panel.style.display = panel.style.display === 'none' ? 'block' : '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: [],
            iref: [],
            directImages: []
        };
    }

    function updatePromptParams() {
        const { prompt, ar, stylize, weird, chaos, mode, draft, noPrompt, version, cref, sref, speed, oref, iref, directImages } = 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}`
        ];

        // 处理带权重的图像参考
        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;
            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;
        `;

        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>
            </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>
                    <label>风格化 <input type="range" id="stylize" min="0" max="1000" value="100"></label><br>
                    <label>奇特化 <input type="range" id="weird" min="0" max="3000" value="0"></label><br>
                    <label>多样性 <input type="range" id="chaos" min="0" max="100" value="0"></label>
                </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;">速度
                            <select id="speed-select" style="width:100%; padding:6px;">
                                <option value="relax">标准</option>
                                <option value="fast">快速</option>
                                <option value="turbo">极速</option>
                            </select>
                        </label>
                        <label style="flex:1;">模式
                            <select id="mode-select" style="width:100%; padding:6px;">
                                <option value="standard">标准</option>
                                <option value="raw">原始</option>
                            </select>
                        </label>
                    </div>
                    <div style="display:flex; gap:10px; align-items:center;">
                        <label style="flex:1;">草稿
                            <input type="checkbox" id="draft-toggle">
                        </label>
                        <label style="flex:2;">版本
                            <select id="version-select" style="width:100%; padding:6px;">
                                <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;">
                        <input type="number" id="cref-weight" placeholder="权重" style="flex:1; padding:6px;">
                    </div>
                    <button id="cref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer;">添加</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;">
                        <input type="number" id="sref-weight" placeholder="权重" style="flex:1; padding:6px;">
                    </div>
                    <button id="sref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer;">添加</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;">
                        <input type="number" id="oref-weight" placeholder="权重" style="flex:1; padding:6px;">
                    </div>
                    <button id="oref-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer;">添加</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;">
                        <input type="number" id="direct-image-weight" placeholder="权重" style="flex:1; padding:6px;">
                    </div>
                    <button id="direct-image-add" style="margin-top:6px; padding:4px 8px; background:#4f46e5; color:white; border:none; border-radius:4px; cursor:pointer;">添加</button>
                    <div id="direct-image-preview" style="margin-top:10px; display:flex; flex-wrap:wrap; gap:8px;"></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;"></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;"></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;" 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;">拷贝参数</button>
                <button id="clear-btn" style="flex:1; padding:8px; background:#e5e7eb; color:#111827; border:none; border-radius:6px; cursor:pointer;">清空参数</button>
            </div>
        `;

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

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

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

        // 绑定已有控件事件
        $('stylize').oninput = e => { params.stylize = +e.target.value; updatePromptParams(); };
        $('weird').oninput = e => { params.weird = +e.target.value; updatePromptParams(); };
        $('chaos').oninput = e => { params.chaos = +e.target.value; updatePromptParams(); };
        $('mode-select').onchange = e => { params.mode = e.target.value; updatePromptParams(); };
        $('version-select').onchange = e => { params.version = e.target.value; updatePromptParams(); };
        $('draft-toggle').onchange = e => { params.draft = e.target.checked; updatePromptParams(); };
        $('speed-select').onchange = e => { params.speed = 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');
            alert('参数已复制!');
        };

        // 重置所有控件状态
        $('clear-btn').onclick = () => {
            resetParams();

            // 重置所有控件为默认值
            $('main-prompt').value = '';
            $('stylize').value = 100;
            $('weird').value = 0;
            $('chaos').value = 0;
            $('mode-select').value = 'standard';
            $('version-select').value = 'v7';
            $('draft-toggle').checked = false;
            $('speed-select').value = 'relax';
            $('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 = '';

            // 触发事件以更新UI
            $('main-prompt').dispatchEvent(new Event('input'));
            $('stylize').dispatchEvent(new Event('input'));
            $('weird').dispatchEvent(new Event('input'));
            $('chaos').dispatchEvent(new Event('input'));
            $('mode-select').dispatchEvent(new Event('change'));
            $('version-select').dispatchEvent(new Event('change'));
            $('draft-toggle').dispatchEvent(new Event('change'));
            $('speed-select').dispatchEvent(new Event('change'));
            $('no-prompt').dispatchEvent(new Event('input'));
            $('ratio-slider').dispatchEvent(new Event('input'));

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

            // 刷新预览
            refreshPreviews();

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

        // 绑定角色参考事件
        $('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 {
                    alert('该URL已添加');
                }
            } else {
                alert('请输入有效图片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 {
                    alert('该URL已添加');
                }
            } else {
                alert('请输入有效图片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 {
                    alert('该URL已添加');
                }
            } else {
                alert('请输入有效图片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 {
                    alert('该URL已添加');
                }
            } else {
                alert('请输入有效图片URL');
            }
        };

        // 绑定比例滑块事件
        const sizeMap = ['1:2', '19:6', '2:3', '3:4', '5:6', '1:1', '6:5', '4:3', '3:2', '6:19', '2:1'];
        const ratioPresets = {
            '1:2': { width: 50, height: 100 },
            '19:6': { 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 },
            '6:19': { 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;
            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.style.cssText = 'padding:4px 12px; border-radius:6px; border:1px solid #d1d5db; background:white; cursor:pointer;';

            // 修复:正确映射预设比例
            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);
        });
    }

    // 添加预览图片并绑定删除功能
    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';

        // 添加权重显示
        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(0,0,0,0.7); color: white; border: none; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; line-height: 1; cursor: pointer;';
        deleteBtn.textContent = '×';
        deleteBtn.onclick = function(e) {
            e.stopPropagation();
            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();
            }
        };

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

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