Greasy Fork

Greasy Fork is available in English.

Discord Midjourney 参数面板

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

当前为 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 和最新参数功能(v7 支持)
// @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 = {};

    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 = {
            ar: '1:1',
            stylize: 100,
            weird: 0,
            chaos: 0,
            mode: 'standard',
            version: 'v7',
            speed: 'relax',
            draft: false,
            noPrompt: '',
            cref: [],
            sref: []
        };
    }

    function updatePromptParams() {
        const { ar, stylize, weird, chaos, mode, draft, noPrompt, version, cref, sref, speed } = params;

        // 优先处理URL参数(cref和sref)
        const urlParts = [
            ...cref.map(url => `--cref ${url}`),
            ...sref.map(url => `--sref ${url}`)
        ];

        // 处理其他参数
        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 promptField = document.getElementById('prompt-params');
        if (promptField) {
            const allParts = [...urlParts, ...otherParts.filter(Boolean)];
            promptField.value = allParts.join(' ');
        }
    }

    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>
                    <input type="text" id="cref-url" placeholder="粘贴角色图片URL" style="width:100%; padding:6px;">
                    <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>
                    <input type="text" id="sref-url" placeholder="粘贴风格图片URL" style="width:100%; padding:6px;">
                    <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>

            <div style="margin-top:20px; border-top:1px solid #e5e7eb; padding-top:16px; display:flex; gap:16px;">
                <textarea id="prompt-params" style="flex:2; height:80px; padding:10px;" readonly></textarea>
                <textarea id="no-prompt" placeholder="输入需要排除的元素,多个用空格分隔" style="flex:1; height:80px; padding:10px;"></textarea>
            </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);

        // 绑定已有控件事件
        $('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();

            // 重置所有控件为默认值
            $('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;

            // 触发事件以更新UI
            $('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();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.cref.includes(url)) {
                    params.cref.push(url);
                    addPreviewImage('cref-preview', url, 'cref');
                    $('cref-url').value = '';
                    updatePromptParams();
                } else {
                    alert('该URL已添加');
                }
            } else {
                alert('请输入有效图片URL');
            }
        };

        // 绑定风格参考事件
        $('sref-add').onclick = () => {
            const url = $('sref-url').value.trim();
            if (url && /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url)) {
                if (!params.sref.includes(url)) {
                    params.sref.push(url);
                    addPreviewImage('sref-preview', url, 'sref');
                    $('sref-url').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, paramType) {
        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 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.indexOf(url)
                : params.sref.indexOf(url);

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

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

    // 刷新预览区域
    function refreshPreviews() {
        ['cref', 'sref'].forEach(type => {
            const container = document.getElementById(`${type}-preview`);
            container.innerHTML = '';
            params[type].forEach(url => {
                addPreviewImage(`${type}-preview`, url, type);
            });
        });
    }

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

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