// ==UserScript== // @name Discord Midjourney 参数可视化 // @namespace https://github.com/cwser // @version 1.0.1 // @description 在 Discord Midjourney 频道添加一个参数面板... // @author cwser // @match https://discord.com/* // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/535676/Discord%20Midjourney%20%E5%8F%82%E6%95%B0%E5%8F%AF%E8%A7%86%E5%8C%96.user.js // @updateURL https://update.greasyfork.icu/scripts/535676/Discord%20Midjourney%20%E5%8F%82%E6%95%B0%E5%8F%AF%E8%A7%86%E5%8C%96.meta.js // ==/UserScript== (function() { 'use strict'; // 参数定义 let params = { prompt: '', ar: '1:1', stylize: 100, weird: 0, chaos: 0, mode: 'standard', version: 'v7', speed: 'relax', draft: false, noPrompt: '', cref: [], // 每一项将是 { url: string, weight: string, enabled: boolean } sref: [], // 每一项将是 { url: string, weight: string, enabled: boolean } oref: [], // 每一项将是 { url: string, weight: string, enabled: boolean } iref: [], // iref 似乎未在UI中使用,但保留定义 directImages: [], // 每一项将是 { url: string, weight: string, enabled: boolean } tile: false, seed: '', quality: 1, stop: 100, visibility: '', personalParams: '', r: 1, includeImagine: false // 用于控制是否添加 /imagine prompt: 前缀 }; // 主题相关变量 let currentThemeMode = 'discord'; // 选项: 'light', 'dark', 'discord', 'system' const themeStorageKey = 'mjPanelThemePreference_v3'; // 更新存储键 const themeModes = ['light', 'dark', 'discord', 'system']; const themeTextMap = { 'light': '浅色模式', 'dark': '深色模式', 'discord': '跟随Discord', 'system': '跟随系统' }; const sunIconSVG = ``; const moonIconSVG = ``; const discordIconSVG = ``; const systemIconSVG = ``; const themeIcons = { 'light': sunIconSVG, 'dark': moonIconSVG, 'discord': discordIconSVG, 'system': systemIconSVG }; let systemThemeMediaQuery = null; let discordThemeObserver = null; // 定义权重参数前缀的映射 const weightPrefixes = { 'cref': '--cw', 'sref': '--sw', 'oref': '--ow', 'directImages': '--iw' // directImages 使用 --iw }; // 显示 Toast 提示 function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; toast.className = 'mj-toast'; // 使用类进行样式化 toast.style.cssText = `transform: translateY(-20px); opacity: 0;`; // 初始状态,CSS会覆盖 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(() => { if (document.body.contains(toast)) document.body.removeChild(toast); }, 300); }, 2000); } // 创建设置按钮 function createSettingButton() { const button = document.createElement('button'); button.textContent = 'MJ参数'; button.id = 'mj-floating-settings-button'; button.addEventListener('click', toggleControlPanel); document.body.appendChild(button); button.addEventListener('mouseenter', () => button.style.transform = 'scale(1.05)'); button.addEventListener('mouseleave', () => 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 { const themeMenu = document.getElementById('theme-options-menu'); if (themeMenu) themeMenu.style.display = 'none'; // 关闭主题菜单 panel.style.opacity = '0'; panel.style.transform = 'translateY(10px)'; // 离场动画 setTimeout(() => { panel.style.display = 'none'; }, 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: '', r: 1, includeImagine: false }; } // 更新最终的提示词参数字符串 function updatePromptParams() { const { prompt, ar, stylize, weird, chaos, mode, draft, noPrompt, version, speed, tile, seed, quality, stop, visibility, personalParams, includeImagine } = params; // 直接从全局params对象获取数组,因为它们现在包含更多状态 (url, weight, enabled) const { cref, sref, oref, directImages } = params; const otherParts = [ ar ? `--ar ${ar}` : '', `--s ${stylize}`, // 假设stylize总是有值,且其默认值100是期望的 weird !== 0 ? `--w ${weird}` : '', chaos !== 0 ? `--c ${chaos}` : '', mode !== 'standard' ? `--${mode}` : '', draft ? '--draft' : '', noPrompt ? `--no ${noPrompt}` : '', version.startsWith('niji') ? `--niji ${version.replace('niji', '')}` : `--v ${version.replace('v', '')}`, speed ? `--${speed}` : '', // 假设 'relax' 是默认值,如果不想显示默认的relax,这里需要调整 tile ? '--tile' : '', seed ? `--seed ${seed}` : '', quality !== 1 ? `--q ${quality}` : '', stop !== 100 ? `--stop ${stop}` : '', visibility ? `--${visibility}` : '', personalParams ? `--p ${personalParams}` : '', params.r > 1 ? `--r ${params.r}` : '' ].filter(Boolean); const formatImageWithWeight = (url, weightValue, prefix) => { const weightStr = (typeof weightValue === 'string' || typeof weightValue === 'number') ? String(weightValue).trim() : ''; return `${url}${weightStr !== '' ? ` ${prefix} ${weightStr}` : ''}`; }; const directImageUrls = directImages .filter(item => item.enabled) // 只包含启用的 .map(item => formatImageWithWeight(item.url, item.weight, '--iw')) .join(' '); const crefUrls = cref .filter(item => item.enabled) .map(item => `--cref ${formatImageWithWeight(item.url, item.weight, '--cw')}`); const srefUrls = sref .filter(item => item.enabled) .map(item => `--sref ${formatImageWithWeight(item.url, item.weight, '--sw')}`); const orefUrls = oref .filter(item => item.enabled) .map(item => `--oref ${formatImageWithWeight(item.url, item.weight, '--ow')}`); const promptField = document.getElementById('prompt-params'); if (promptField) { const allParts = [ directImageUrls, prompt.trim(), ...crefUrls, ...srefUrls, ...orefUrls, ...otherParts ].filter(Boolean); let finalPromptString = allParts.join(' ').trim().replace(/\s+/g, ' '); // 规范化空格 if (includeImagine && finalPromptString) { finalPromptString = `/imagine prompt: ${finalPromptString}`; } promptField.value = finalPromptString; } } // 获取实际生效的暗黑模式状态 function getEffectiveDarkModeState() { switch (currentThemeMode) { case 'light': return false; case 'dark': return true; case 'discord': return document.documentElement.classList.contains('theme-dark'); case 'system': return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; default: return document.documentElement.classList.contains('theme-dark'); } } // 应用当前主题 function applyCurrentTheme() { const panel = document.getElementById('mj-control-panel'); if (!panel) return; const effectiveDarkMode = getEffectiveDarkModeState(); panel.classList.toggle('dark-mode', effectiveDarkMode); localStorage.setItem(themeStorageKey, currentThemeMode); const themeTriggerButton = document.getElementById('theme-dropdown-trigger'); const themeTriggerIcon = document.getElementById('theme-trigger-icon'); const themeTriggerText = document.getElementById('theme-trigger-text'); if (themeTriggerIcon) { let iconToShow = themeIcons[currentThemeMode] || sunIconSVG; if (currentThemeMode === 'discord' || currentThemeMode === 'system') { iconToShow = effectiveDarkMode ? moonIconSVG : sunIconSVG; } themeTriggerIcon.innerHTML = iconToShow; } if (themeTriggerText) { themeTriggerText.textContent = themeTextMap[currentThemeMode] || '未知主题'; } if (themeTriggerButton) { themeTriggerButton.title = `切换主题 (当前: ${themeTextMap[currentThemeMode]})`; } const themeOptions = document.querySelectorAll('#theme-options-menu button'); themeOptions.forEach(opt => { opt.classList.toggle('active', opt.dataset.theme === currentThemeMode); }); refreshPreviews(); // 主题变化时也刷新预览,确保预览内控件样式正确 setInitialActiveButtons(); updateToggleVisuals('tile-toggle-switch', 'tile'); updateToggleVisuals('draft-toggle-switch', 'draft'); updateToggleVisuals('imagine-toggle-switch', 'includeImagine'); setupDynamicThemeListeners(); } // 处理系统主题变化 function handleSystemThemeChange() { if (currentThemeMode === 'system') applyCurrentTheme(); } // 处理Discord主题变化 function handleDiscordThemeChange(mutationsList) { if (currentThemeMode === 'discord') { for (const mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { applyCurrentTheme(); break; } } } } // 设置动态主题监听器 function setupDynamicThemeListeners() { if (systemThemeMediaQuery) { systemThemeMediaQuery.removeEventListener ? systemThemeMediaQuery.removeEventListener('change', handleSystemThemeChange) : systemThemeMediaQuery.removeListener(handleSystemThemeChange); systemThemeMediaQuery = null; } if (discordThemeObserver) { discordThemeObserver.disconnect(); discordThemeObserver = null; } if (currentThemeMode === 'system' && window.matchMedia) { systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); systemThemeMediaQuery.addEventListener ? systemThemeMediaQuery.addEventListener('change', handleSystemThemeChange) : systemThemeMediaQuery.addListener(handleSystemThemeChange); } else if (currentThemeMode === 'discord' && typeof MutationObserver !== "undefined") { discordThemeObserver = new MutationObserver(handleDiscordThemeChange); discordThemeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); } } // 创建控制面板HTML结构 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); 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: opacity 0.2s ease, transform 0.2s ease; `; panel.innerHTML = `

Midjourney 参数设置

图片尺寸 (--ar)

1:1

美学参数

100
0
0

模型设置

角色参考 (--cref)

风格参考 (--sref)

全方位参考 (--oref)

图像参考 (URL --iw)

更多参数

1
100
1

可见性 (--public, --stealth)

最终参数

提示词

排除词 (--no)

`; document.body.appendChild(panel); const savedTheme = localStorage.getItem(themeStorageKey); if (savedTheme && themeModes.includes(savedTheme)) { currentThemeMode = savedTheme; } else { currentThemeMode = 'discord'; // 默认跟随Discord } bindControlEvents(); applyCurrentTheme(); // 初始化时应用主题 // 点击面板外部关闭主题菜单 document.addEventListener('click', function(event) { const themeMenu = document.getElementById('theme-options-menu'); const themeTrigger = document.getElementById('theme-dropdown-trigger'); if (themeMenu && themeTrigger && themeMenu.style.display === 'block') { if (!themeMenu.contains(event.target) && !themeTrigger.contains(event.target)) { themeMenu.style.display = 'none'; } } }); } function setInitialActiveButtons() { const buttonGroups = [ { className: 'speed-btn', param: 'speed', defaultValue: 'relax' }, { className: 'mode-btn', param: 'mode', defaultValue: 'standard' }, { className: 'visibility-btn', param: 'visibility', defaultValue: '' } ]; buttonGroups.forEach(group => { const buttons = document.querySelectorAll(`#mj-control-panel .${group.className}`); let currentParamValue = params[group.param] === undefined ? group.defaultValue : params[group.param]; buttons.forEach(btn => { btn.classList.toggle('active', btn.dataset.value === currentParamValue); }); }); // 确保比例滑块在初始化时触发input事件来更新预览和参数 const ratioSlider = document.getElementById('ratio-slider'); if (ratioSlider) ratioSlider.dispatchEvent(new Event('input')); } function updateToggleVisuals(switchId, property) { const switchEl = document.getElementById(switchId); if (!switchEl) return; // 确保 params[property] 是布尔值进行比较 switchEl.classList.toggle('active', !!params[property]); } const setupRefSection = (idPrefix, paramKey) => { const $ = id => document.getElementById(id); const addBtn = $(`${idPrefix}-add`); const urlInput = $(`${idPrefix}-url`); const weightInput = $(`${idPrefix}-weight`); // 这是用于添加新条目的权重输入框 const previewContainerId = `${idPrefix}-preview`; if (!addBtn || !urlInput || !weightInput) return; addBtn.onclick = () => { const url = urlInput.value.trim(); const weight = weightInput.value.trim(); // 新条目的初始权重 let toastMessage = `请输入${paramKey === 'sref' ? 'URL或代码' : 'URL'}`; if (!url) { showToast(toastMessage); return; } const isImageUrl = /^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg|bmp|tiff|ico)(\?.*)?$/i.test(url); const isSrefCode = paramKey === 'sref' && (/^\d+$/.test(url) || url.toLowerCase() === 'random'); if (paramKey !== 'sref' && !isImageUrl) { showToast('请输入有效的图片URL'); return; } if (paramKey === 'sref' && !isImageUrl && !isSrefCode) { showToast("sref请输入有效图片URL, 'random'或数字代码"); return; } const targetArray = params[paramKey]; if (!Array.isArray(targetArray)) return; // 应该不会发生 const itemUrl = (paramKey === 'sref' && isSrefCode) ? (url.toLowerCase() === 'random' ? 'random' : url) : url; if (!targetArray.some(item => item.url === itemUrl)) { const newItem = { url: itemUrl, weight: weight, enabled: true }; // 添加 enabled 状态 targetArray.push(newItem); const currentWeightPrefix = weightPrefixes[paramKey]; if (paramKey === 'sref' && isSrefCode) { addPreviewSrefCode(previewContainerId, newItem, paramKey); // 传递整个 newItem 对象 } else { addPreviewImage(previewContainerId, newItem, paramKey, currentWeightPrefix); // 传递整个 newItem 对象 } urlInput.value = ''; weightInput.value = ''; // 清空外部输入框 updatePromptParams(); } else { showToast(`该${(paramKey === 'sref' && isSrefCode) ? '代码' : 'URL'}已添加`); } }; }; function bindControlEvents() { const $ = id => document.getElementById(id); // 主题切换 const themeTriggerButton = $('theme-dropdown-trigger'); const themeOptionsMenu = $('theme-options-menu'); if (themeTriggerButton && themeOptionsMenu) { themeTriggerButton.addEventListener('click', (event) => { event.stopPropagation(); // 防止触发 document 的 click 事件 themeOptionsMenu.style.display = themeOptionsMenu.style.display === 'none' ? 'block' : 'none'; }); document.querySelectorAll('.theme-option-button').forEach(button => { button.addEventListener('click', () => { currentThemeMode = button.dataset.theme; applyCurrentTheme(); themeOptionsMenu.style.display = 'none'; // 关闭菜单 }); }); } if ($('main-prompt')) $('main-prompt').oninput = e => { params.prompt = e.target.value; updatePromptParams(); }; const bindSliderEvent = (id, property, displayId = null, isFloat = false) => { const slider = $(id); if (!slider) return; slider.oninput = e => { const value = isFloat ? parseFloat(e.target.value) : parseInt(e.target.value, 10); params[property] = value; if (displayId && $(displayId)) $(displayId).textContent = e.target.value; updatePromptParams(); }; }; bindSliderEvent('stylize', 'stylize', 'stylize-value'); bindSliderEvent('weird', 'weird', 'weird-value'); bindSliderEvent('chaos', 'chaos', 'chaos-value'); bindSliderEvent('stop-slider', 'stop', 'stop-value'); bindSliderEvent('r-slider', 'r', 'r-value'); const qualitySlider = $('quality-slider'); const qualityValueDisplay = $('quality-value'); const qualityMap = [0.25, 0.5, 1, 2, 4]; if (qualitySlider && qualityValueDisplay) { // 初始化 const initialQualityValue = params.quality; const initialSliderIndex = qualityMap.indexOf(initialQualityValue); if (initialSliderIndex !== -1) { qualitySlider.value = initialSliderIndex; qualityValueDisplay.textContent = initialQualityValue; } else { // 如果 params.quality 不是预设值,则重置为1 params.quality = 1; qualitySlider.value = qualityMap.indexOf(1); // Should be 2 qualityValueDisplay.textContent = params.quality; } qualitySlider.oninput = e => { const sliderIndex = parseInt(e.target.value, 10); if (sliderIndex >= 0 && sliderIndex < qualityMap.length) { params.quality = qualityMap[sliderIndex]; if (qualityValueDisplay) qualityValueDisplay.textContent = params.quality; updatePromptParams(); } }; } const bindRadioGroup = (groupClass, property) => { document.querySelectorAll(`#mj-control-panel .${groupClass}`).forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll(`#mj-control-panel .${groupClass}`).forEach(b => b.classList.remove('active')); btn.classList.add('active'); params[property] = btn.dataset.value; updatePromptParams(); }); }); }; bindRadioGroup('speed-btn', 'speed'); bindRadioGroup('mode-btn', 'mode'); bindRadioGroup('visibility-btn', 'visibility'); const bindToggleSwitchEvent = (switchId, property) => { const switchEl = $(switchId); if (!switchEl) return; switchEl.addEventListener('click', () => { params[property] = !params[property]; updateToggleVisuals(switchId, property); // 更新视觉 updatePromptParams(); }); }; bindToggleSwitchEvent('tile-toggle-switch', 'tile'); bindToggleSwitchEvent('draft-toggle-switch', 'draft'); bindToggleSwitchEvent('imagine-toggle-switch', 'includeImagine'); if ($('version-select')) $('version-select').onchange = e => { params.version = e.target.value; updatePromptParams(); }; if ($('no-prompt')) $('no-prompt').oninput = e => { params.noPrompt = e.target.value.trim(); updatePromptParams(); }; if ($('copy-btn')) { $('copy-btn').onclick = () => { const textarea = $('prompt-params'); if (!textarea || !textarea.value) { showToast('没有参数可以拷贝'); return; } textarea.select(); try { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(textarea.value) .then(() => showToast('参数已复制!')) .catch(() => { showToast('复制失败!请尝试手动复制。'); legacyCopy(textarea); }); } else { legacyCopy(textarea); // 兼容旧版浏览器 } } catch (err) { console.error('复制尝试失败:', err); // showToast('复制异常!请尝试手动复制。'); // 已经在 legacyCopy 中处理 } }; } function legacyCopy(textareaElement) { try { const successful = document.execCommand('copy'); showToast(successful ? '参数已复制 (兼容模式)!' : '复制失败 (兼容模式)!请手动复制。'); } catch (err) { showToast('复制异常 (兼容模式)!请手动复制。'); } } const sizeMap = ['1:2', '9:16', '2:3', '3:4', '5:6', '1:1', '6:5', '4:3', '3:2', '16:9', '2:1']; if ($('clear-btn')) { $('clear-btn').onclick = () => { resetParams(); // 重置数据模型 // 更新UI元素以反映重置后的默认值 if ($('main-prompt')) $('main-prompt').value = params.prompt; if ($('stylize')) $('stylize').value = params.stylize; if ($('stylize-value')) $('stylize-value').textContent = params.stylize; if ($('weird')) $('weird').value = params.weird; if ($('weird-value')) $('weird-value').textContent = params.weird; if ($('chaos')) $('chaos').value = params.chaos; if ($('chaos-value')) $('chaos-value').textContent = params.chaos; if ($('version-select')) $('version-select').value = params.version; if ($('no-prompt')) $('no-prompt').value = params.noPrompt; if ($('ratio-slider')) { // 更新比例滑块和预览 const defaultRatioIndex = sizeMap.indexOf(params.ar); $('ratio-slider').value = defaultRatioIndex !== -1 ? defaultRatioIndex : 5; // 5 is '1:1' $('ratio-slider').dispatchEvent(new Event('input')); // 触发更新 } // 清空参考URL和权重的输入框 ['cref-url', 'cref-weight', 'sref-url', 'sref-weight', 'oref-url', 'oref-weight', 'direct-image-url', 'direct-image-weight'] .forEach(id => { if ($(id)) $(id).value = ''; }); if ($('seed-input')) $('seed-input').value = params.seed; // 更新质量滑块 if ($('quality-slider')) { const resetSliderIndex = qualityMap.indexOf(params.quality); // params.quality is 1 $('quality-slider').value = resetSliderIndex !== -1 ? resetSliderIndex : qualityMap.indexOf(1); // index for 1 is 2 const qvDisplay = $('quality-value'); if(qvDisplay) qvDisplay.textContent = params.quality; } if ($('stop-slider')) $('stop-slider').value = params.stop; if ($('stop-value')) $('stop-value').textContent = params.stop; if ($('personal-params')) $('personal-params').value = params.personalParams; if ($('r-slider')) $('r-slider').value = params.r; if ($('r-value')) $('r-value').textContent = params.r; // 更新所有切换开关的视觉状态 updateToggleVisuals('tile-toggle-switch', 'tile'); updateToggleVisuals('draft-toggle-switch', 'draft'); updateToggleVisuals('imagine-toggle-switch', 'includeImagine'); setInitialActiveButtons(); // 重置按钮组状态 refreshPreviews(); // 清空并重建所有参考图预览 updatePromptParams(); // 更新最终参数显示 showToast('所有参数已重置为默认值'); }; } setupRefSection('cref', 'cref'); setupRefSection('sref', 'sref'); setupRefSection('oref', 'oref'); setupRefSection('direct-image', 'directImages'); 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 }}; const ratioSlider = $('ratio-slider'); if (ratioSlider) { ratioSlider.oninput = e => { const ratio = sizeMap[+e.target.value] || '1:1'; params.ar = ratio; const box = $('ratio-box'); const preset = ratioPresets[ratio] || { width: 100, height: 100 }; if(box) { box.style.width = `${preset.width}px`; box.style.height = `${preset.height}px`; box.textContent = ratio; } // 更新按钮组的激活状态 document.querySelectorAll('#size-buttons button').forEach(btn => { btn.classList.toggle('active', btn.dataset.value === ratio); }); updatePromptParams(); }; } const sizeButtonGroup = $('size-buttons'); if (sizeButtonGroup) { const presetMap = { '纵向': '1:2', '正方形': '1:1', '横向': '2:1' }; ['纵向', '正方形', '横向'].forEach((label) => { const btn = document.createElement('button'); btn.textContent = label; btn.dataset.value = presetMap[label]; btn.onclick = () => { const ratio = presetMap[label]; const sliderIndex = sizeMap.indexOf(ratio); if (sliderIndex !== -1 && ratioSlider) { ratioSlider.value = sliderIndex; ratioSlider.dispatchEvent(new Event('input')); // 触发滑块的input事件来更新所有 } }; sizeButtonGroup.appendChild(btn); }); } if ($('seed-input')) { $('seed-input').oninput = e => { const value = e.target.value.trim(); if (/^\d*$/.test(value) && (value === '' || (parseInt(value) >= 0 && parseInt(value) <= 4294967295))) { params.seed = value; // 允许为空字符串,表示不设置seed } else { // 如果输入无效,则恢复到上一个有效值 e.target.value = params.seed; } updatePromptParams(); }; } if ($('personal-params')) $('personal-params').oninput = e => { params.personalParams = e.target.value.trim(); updatePromptParams(); }; // 点击最终参数文本区直接复制 const promptParamsTextarea = $('prompt-params'); if (promptParamsTextarea) { promptParamsTextarea.addEventListener('click', () => { if (!promptParamsTextarea.value) { showToast('没有参数可以拷贝'); return; } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(promptParamsTextarea.value) .then(() => showToast('参数已复制!')) .catch(() => { showToast('复制失败!'); legacyCopy(promptParamsTextarea);}); } else { legacyCopy(promptParamsTextarea); } }); } // 给所有输入框、文本区、下拉框添加聚焦时的视觉效果 document.querySelectorAll('#mj-control-panel input[type="text"], #mj-control-panel input[type="number"], #mj-control-panel textarea, #mj-control-panel select').forEach(el => { el.addEventListener('focus', (e) => e.target.classList.add('focused')); el.addEventListener('blur', (e) => e.target.classList.remove('focused')); }); // 初始化切换开关的状态 updateToggleVisuals('tile-toggle-switch', 'tile'); updateToggleVisuals('draft-toggle-switch', 'draft'); updateToggleVisuals('imagine-toggle-switch', 'includeImagine'); // 初始化按钮组和滑块状态 setInitialActiveButtons(); if (ratioSlider) { // 确保比例滑块在加载时也正确设置其初始值和预览 const initialRatioIndex = sizeMap.indexOf(params.ar); ratioSlider.value = initialRatioIndex !== -1 ? initialRatioIndex : 5; // 5 is '1:1' ratioSlider.dispatchEvent(new Event('input')); } } // 添加图片预览到容器 (包含启用/禁用和权重调整) function addPreviewImage(containerId, item, paramKey, currentWeightPrefix) { const container = document.getElementById(containerId); if (!container) return; const imgContainer = document.createElement('div'); imgContainer.className = 'ref-image-container'; imgContainer.style.animation = 'fadeIn 0.3s ease'; if (item.enabled === false) { // 显式检查 false imgContainer.classList.add('disabled-ref-item'); } const img = document.createElement('img'); img.src = item.url; img.className = 'ref-image-preview'; img.onerror = () => { // 处理图片加载失败 const errorDiv = document.createElement('div'); errorDiv.textContent = '无效图'; errorDiv.className = 'ref-image-error'; if (imgContainer.contains(img)) imgContainer.removeChild(img); // 移除损坏的img标签 // 尝试在控件前插入错误提示,如果控件容器已创建 const controlsNode = imgContainer.querySelector('.ref-item-controls'); if (controlsNode) { imgContainer.insertBefore(errorDiv, controlsNode); } else { // 否则,作为第一个子元素插入(或根据实际布局调整) imgContainer.insertBefore(errorDiv, imgContainer.firstChild); } }; // 控件容器 (用于权重输入和禁用开关) const controlsContainer = document.createElement('div'); controlsContainer.className = 'ref-item-controls'; // 权重输入框 const weightInputPreview = document.createElement('input'); weightInputPreview.type = 'number'; weightInputPreview.className = 'ref-item-weight-input'; weightInputPreview.placeholder = 'W'; // 简短占位符 weightInputPreview.value = item.weight || ''; weightInputPreview.title = `调整权重 (${currentWeightPrefix || weightPrefixes[paramKey] || '--iw'})`; weightInputPreview.oninput = (e) => { item.weight = e.target.value.trim(); updatePromptParams(); // 更新旁边的徽章文本 const badge = imgContainer.querySelector('.ref-weight-badge'); if (badge) { const wP = (currentWeightPrefix ? currentWeightPrefix.replace('--', '') : (weightPrefixes[paramKey] ? weightPrefixes[paramKey].replace('--','') : 'iw')); badge.textContent = item.weight && item.weight.trim() !== '' ? `${wP}:${item.weight}` : `${wP}:默认`; } }; weightInputPreview.onclick = (e) => e.stopPropagation(); // 防止点击输入框时触发容器的事件 // 禁用/启用 开关 const disableToggle = document.createElement('div'); disableToggle.className = 'ref-item-disable-toggle toggle-switch'; // 复用 .toggle-switch 样式 if (item.enabled) disableToggle.classList.add('active'); disableToggle.title = item.enabled ? '点击禁用' : '点击启用'; const toggleDot = document.createElement('div'); toggleDot.className = 'toggle-dot'; disableToggle.appendChild(toggleDot); disableToggle.onclick = (e) => { e.stopPropagation(); item.enabled = !item.enabled; disableToggle.classList.toggle('active', item.enabled); imgContainer.classList.toggle('disabled-ref-item', !item.enabled); disableToggle.title = item.enabled ? '点击禁用' : '点击启用'; updatePromptParams(); }; controlsContainer.appendChild(weightInputPreview); controlsContainer.appendChild(disableToggle); // 权重徽章 (保持原有逻辑,显示前缀和权重) const weightBadge = document.createElement('div'); weightBadge.className = 'ref-weight-badge'; const wP = (currentWeightPrefix ? currentWeightPrefix.replace('--', '') : (weightPrefixes[paramKey] ? weightPrefixes[paramKey].replace('--','') : 'iw')); weightBadge.textContent = item.weight && item.weight.trim() !== '' ? `${wP}:${item.weight}` : `${wP}:默认`; // 删除按钮 const deleteBtn = document.createElement('button'); deleteBtn.className = 'ref-delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.title = '删除此参考'; deleteBtn.onclick = function(e) { e.stopPropagation(); imgContainer.style.animation = 'fadeOut 0.2s ease forwards'; setTimeout(() => { const targetArray = params[paramKey]; if (Array.isArray(targetArray)) { const index = targetArray.findIndex(i => i.url === item.url); // 通过url查找 if (index !== -1) { targetArray.splice(index, 1); if (container.contains(imgContainer)) container.removeChild(imgContainer); updatePromptParams(); } } }, 200); }; imgContainer.appendChild(img); // 先添加图片本身 imgContainer.appendChild(controlsContainer); // 再添加控件容器 imgContainer.appendChild(weightBadge); // 然后是权重徽章 imgContainer.appendChild(deleteBtn); // 最后是删除按钮 container.appendChild(imgContainer); } // 添加sref代码预览到容器 (包含启用/禁用和权重调整) function addPreviewSrefCode(containerId, item, paramKey) { const container = document.getElementById(containerId); if (!container) return; const codeContainer = document.createElement('div'); codeContainer.className = 'ref-code-container'; codeContainer.style.animation = 'fadeIn 0.3s ease'; if (item.enabled === false) { // 显式检查 false codeContainer.classList.add('disabled-ref-item'); } const codeText = document.createElement('span'); codeText.className = 'ref-code-text-main'; // 给文本加个class方便控制样式 codeText.textContent = `sref:${item.url}`; // 控件容器 const controlsContainer = document.createElement('div'); controlsContainer.className = 'ref-item-controls sref-controls'; // 可以添加 sref 特有的 class // sref 权重输入框 const weightInputPreview = document.createElement('input'); weightInputPreview.type = 'number'; weightInputPreview.className = 'ref-item-weight-input sref-weight-input'; weightInputPreview.placeholder = 'W'; weightInputPreview.value = item.weight || ''; weightInputPreview.title = '调整权重 (--sw)'; weightInputPreview.oninput = (e) => { item.weight = e.target.value.trim(); updatePromptParams(); const badge = codeContainer.querySelector('.ref-code-weight-badge'); if (badge) { badge.textContent = item.weight && item.weight.trim() !== '' ? `sw:${item.weight}` : 'sw:默认'; } }; weightInputPreview.onclick = (e) => e.stopPropagation(); // sref 禁用/启用 开关 const disableToggle = document.createElement('div'); disableToggle.className = 'ref-item-disable-toggle toggle-switch'; if (item.enabled) disableToggle.classList.add('active'); disableToggle.title = item.enabled ? '点击禁用' : '点击启用'; const toggleDot = document.createElement('div'); toggleDot.className = 'toggle-dot'; disableToggle.appendChild(toggleDot); disableToggle.onclick = (e) => { e.stopPropagation(); item.enabled = !item.enabled; disableToggle.classList.toggle('active', item.enabled); codeContainer.classList.toggle('disabled-ref-item', !item.enabled); disableToggle.title = item.enabled ? '点击禁用' : '点击启用'; updatePromptParams(); }; controlsContainer.appendChild(weightInputPreview); controlsContainer.appendChild(disableToggle); // sref 权重徽章 const weightBadge = document.createElement('div'); weightBadge.className = 'ref-code-weight-badge'; weightBadge.textContent = item.weight && item.weight.trim() !== '' ? `sw:${item.weight}` : 'sw:默认'; // sref 删除按钮 const deleteBtn = document.createElement('button'); deleteBtn.className = 'ref-code-delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.title = '删除此参考'; deleteBtn.onclick = function(e) { e.stopPropagation(); codeContainer.style.animation = 'fadeOut 0.2s ease forwards'; setTimeout(() => { const targetArray = params[paramKey]; // paramKey 应该是 'sref' const index = targetArray.findIndex(i => i.url === item.url); if (index !== -1) { targetArray.splice(index, 1); if (container.contains(codeContainer)) container.removeChild(codeContainer); updatePromptParams(); } }, 200); }; codeContainer.appendChild(codeText); // sref代码文本 codeContainer.appendChild(controlsContainer); // 控件(权重输入+开关) codeContainer.appendChild(weightBadge); // 权重徽章 codeContainer.appendChild(deleteBtn); // 删除按钮 container.appendChild(codeContainer); } // 刷新所有参考图/代码的预览区域 function refreshPreviews() { const refTypes = [ { idPrefix: 'cref', paramKey: 'cref', previewId: 'cref-preview' }, { idPrefix: 'sref', paramKey: 'sref', previewId: 'sref-preview' }, { idPrefix: 'oref', paramKey: 'oref', previewId: 'oref-preview' }, { idPrefix: 'direct-image', paramKey: 'directImages', previewId: 'direct-image-preview' } ]; refTypes.forEach(ref => { const container = document.getElementById(ref.previewId); if (!container) return; container.innerHTML = ''; // 清空现有预览 const items = params[ref.paramKey]; if (items && Array.isArray(items)) { items.forEach(item => { // item 是 {url, weight, enabled} const currentWeightPrefix = weightPrefixes[ref.paramKey]; // 确保 enabled 属性存在,默认为 true if (typeof item.enabled === 'undefined') { item.enabled = true; } if (ref.paramKey === 'sref' && (item.url === 'random' || /^\d+$/.test(item.url))) { addPreviewSrefCode(ref.previewId, item, ref.paramKey); } else { addPreviewImage(ref.previewId, item, ref.paramKey, currentWeightPrefix); } }); } }); } // 注入CSS样式 function injectStyles() { const styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = ` /* 入场/出场动画 */ @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(5px); } } /* MJ参数浮动按钮 */ #mj-floating-settings-button { position: fixed; right: 20px; bottom: 20px; padding: 10px 20px; background-color: #5865F2; color: white; border: none; border-radius: 8px; cursor: pointer; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: background-color 0.2s ease, transform 0.2s ease; font-family: sans-serif; } #mj-floating-settings-button:hover { background-color: #4752C4; transform: scale(1.05); } /* Toast提示样式 */ .mj-toast { position: fixed; top: 20px; right: 20px; padding: 10px 16px; border-radius: 6px; z-index: 99999; transition: all 0.3s ease; font-family: sans-serif; } #mj-control-panel:not(.dark-mode) ~ body .mj-toast, .mj-toast { /* 默认浅色面板下的Toast,或无面板时的Toast */ background: #2B2D31; color: #DCDDDE; box-shadow: 0 4px 12px rgba(0,0,0,0.15); } #mj-control-panel.dark-mode ~ body .mj-toast { /* 深色面板下的Toast */ background: #DCDDDE; color: #2B2D31; box-shadow: 0 4px 12px rgba(0,0,0,0.3); } /* 面板基础样式 */ #mj-control-panel { background: white; color: #111827; border-color: #E0E0E0; } #mj-control-panel .panel-title { color: #111827; } #mj-control-panel .panel-divider { background: #e5e7eb; } #mj-control-panel .panel-section { background: #f9fafb; padding:16px; border-radius:8px;} #mj-control-panel .section-title { margin:0 0 8px 0; font-weight:500; color: #1F2937; } #mj-control-panel label, #mj-control-panel .param-value-display, #mj-control-panel .imagine-label { color: #1F2937; } #mj-control-panel input[type="text"], #mj-control-panel input[type="number"], #mj-control-panel textarea, #mj-control-panel select { background: white; color: #111827; border: 1px solid #D1D5DB; padding: 6px 4px; border-radius: 6px; box-sizing: border-box; transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease; font-size: 14px; } /* Remove spinners from number inputs in the panel */ #mj-control-panel input[type=number]::-webkit-inner-spin-button, #mj-control-panel input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } #mj-control-panel input[type=number] { -moz-appearance: textfield; /* Firefox */ } #mj-control-panel select { -webkit-appearance: none; -moz-appearance: none; appearance: none; background-image: url('data:image/svg+xml;charset=US-ASCII,'); background-repeat: no-repeat; background-position: right 0.7em top 50%; background-size: 0.8em auto; padding-right: 2.5em; /* 为下拉箭头腾出空间 */ } #mj-control-panel input[type="text"].focused, #mj-control-panel input[type="number"].focused, #mj-control-panel textarea.focused, #mj-control-panel select.focused { border-color: #4f46e5; /* Indigo-600 */ } #mj-control-panel textarea { resize: vertical; height: 80px; width:100%; padding:10px; } #mj-control-panel #prompt-params { background:#f9fafb; cursor:pointer; } /* 提示参数区背景和光标 */ #mj-control-panel .results-section-divider { border-top-color: #e5e7eb; } /* 主题切换触发按钮 */ #mj-control-panel .theme-trigger-btn { padding: 6px 12px; border-radius: 6px; border: 1px solid #D1D5DB; /* Gray-300 */ background-color: white; color: #1F2937; /* Gray-800 */ font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease; } #mj-control-panel .theme-trigger-btn:hover { border-color: #4f46e5; /* Indigo-600 */ } #mj-control-panel .theme-trigger-btn svg { width: 16px; height: 16px; } /* 主题选项菜单 */ #mj-control-panel .theme-options-menu { position: absolute; top: calc(100% + 5px); right: 0; background-color: white; border: 1px solid #D1D5DB; /* Gray-300 */ border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10010; min-width: 180px; padding: 4px; } #mj-control-panel .theme-option-button { display: flex; align-items: center; width: 100%; padding: 8px 12px; text-align: left; background: none; border: none; cursor: pointer; font-size: 13px; color: #1F2937; /* Gray-800 */ border-radius: 4px; transition: background-color 0.15s ease, color 0.15s ease; } #mj-control-panel .theme-option-button svg { margin-right: 8px; width:16px; height:16px; opacity: 0.7; } #mj-control-panel .theme-option-button:hover { background-color: #f0f0f0; /* Lighter gray for hover */ } #mj-control-panel .theme-option-button.active { background-color: #eef2ff; /* Indigo-50 */ color: #4338ca; /* Indigo-700 */ font-weight: 500; } #mj-control-panel .theme-option-button.active svg { opacity: 1; } /* 比例预览 浅色 */ #mj-control-panel .ratio-preview-bg { position:absolute; top:0; left:0; width:100px; height:100px; border:2px dashed #d1d5db; border-radius:12px; } #mj-control-panel .ratio-box { 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; } /* 滑块 浅色 */ #mj-control-panel input[type="range"] { -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; width: 100%;} #mj-control-panel input[type="range"]::-webkit-slider-runnable-track { background: #E5E7EB; height: 0.4rem; border-radius: 0.2rem; } #mj-control-panel input[type="range"]::-moz-range-track { background: #E5E7EB; height: 0.4rem; border-radius: 0.2rem; } #mj-control-panel input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; margin-top: -0.3rem; background-color: #4f46e5; height: 1rem; width: 1rem; border-radius: 50%; border: 1px solid #3730a3; } #mj-control-panel input[type="range"]::-moz-range-thumb { border: none; border-radius: 50%; background-color: #4f46e5; height: 1rem; width: 1rem; border: 1px solid #3730a3;} /* 按钮组 浅色 (通用) */ #mj-control-panel .btn-group { display:flex; border-radius:6px; overflow:hidden; border:1px solid #d1d5db; } #mj-control-panel .btn-group button { flex:1; padding:6px 10px; background:white; border:none; cursor:pointer; color: #374151; transition: background-color 0.2s, color 0.2s; font-size: 13px;} #mj-control-panel .btn-group button:not(:last-child) { border-right: 1px solid #d1d5db; } #mj-control-panel .btn-group button.active { background: #4f46e5; color: white; } /* 切换开关 浅色 */ #mj-control-panel .toggle-switch { position:relative; width:40px; height:20px; border-radius:10px; background:#e5e7eb; cursor:pointer; margin-top: 4px; transition: background-color 0.2s ease; } #mj-control-panel .toggle-switch .toggle-dot { position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:white; box-shadow:0 1px 3px rgba(0,0,0,0.2); transition:all 0.2s ease; } #mj-control-panel .toggle-switch.active { background:#4f46e5; } #mj-control-panel .toggle-switch.active .toggle-dot { transform: translateX(20px); } /* 操作按钮 浅色 */ #mj-control-panel .action-button, #mj-control-panel .action-button-primary, #mj-control-panel .action-button-secondary { padding:4px 8px; border:none; border-radius:4px; cursor:pointer; transition:all 0.2s ease; } #mj-control-panel .action-button { background:#4f46e5; color:white; } /* 添加参考的小按钮 */ #mj-control-panel .action-button:hover { background:#4338CA; } #mj-control-panel .action-button-primary { flex:1; padding:8px; border-radius:6px; background:#4f46e5; color:white; } /* 拷贝 */ #mj-control-panel .action-button-primary:hover { background:#4338CA; } #mj-control-panel .action-button-secondary { flex:1; padding:8px; border-radius:6px; background:#E5E7EB; color:#374151; border: 1px solid #D1D5DB; } /* 清空 */ #mj-control-panel .action-button-secondary:hover { background:#D1D5DB; } #mj-control-panel #size-buttons button { padding: 4px 12px; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; background: white; color: #374151; border: 1px solid #D1D5DB; font-size: 13px;} #mj-control-panel #size-buttons button.active { background: #4f46e5; color: white; border-color: #4f46e5; } #mj-control-panel #size-buttons button:hover:not(.active) { background: #f0f0f0; } /* 参考图预览 浅色 (包含新控件的样式调整) */ #mj-control-panel .ref-preview-container { margin-top:10px; display:flex; flex-wrap:wrap; gap:12px; /* 增加gap以便容纳控件 */ } #mj-control-panel .ref-image-container, #mj-control-panel .ref-code-container { position: relative; margin: 4px; padding-bottom: 26px; /* 为底部控件留出空间 */ min-height: 60px; /* 确保有基础高度 */ min-width: 80px; /* MODIFIED: Ensures enough space for controls */ } #mj-control-panel .ref-image-preview { width: 60px; height: 60px; object-fit: cover; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border: 1px solid #E5E7EB; display: block; /* 防止底部有多余空间 */ } #mj-control-panel .ref-image-preview:hover { transform: scale(1.05); box-shadow: 0 2px 6px rgba(0,0,0,0.15); } #mj-control-panel .ref-image-error { width:60px; height:60px; display:flex; align-items:center; justify-content:center; border-radius:4px; font-size:10px; text-align:center; background: #F3F4F6; color: #757575; border: 1px solid #E5E7EB; box-sizing: border-box; } #mj-control-panel .ref-weight-badge { /* 权重徽章现在位于图片/代码的上方或 کنار */ position: absolute; top: -2px; left: -2px; /* 调整到左上角,稍微偏移 */ background: rgba(255,255,255,0.85); color: #374151; font-size: 9px; padding: 1px 3px; border-radius: 4px 0 4px 0; border-bottom: 1px solid #4f46e5; border-right: 1px solid #4f46e5; z-index: 1; /* 确保在图片之上 */ } #mj-control-panel .ref-delete-btn { position: absolute; top: -6px; right: -6px; background: rgba(239, 68, 68, 0.85); color: white; border: none; border-radius: 50%; width: 18px; height: 18px; font-size: 12px; line-height: 18px; text-align:center; cursor:pointer; transition:all 0.2s ease; opacity: 0.8; display:flex; align-items:center; justify-content:center; box-shadow: 0 1px 2px rgba(0,0,0,0.2); z-index: 2; /* 最高层 */ } #mj-control-panel .ref-delete-btn:hover { opacity: 1; transform: scale(1.1); background: rgb(220, 38, 38); } /* 参考代码预览 浅色 */ #mj-control-panel .ref-code-container { /* 基础样式已在上面与 ref-image-container 合并 */ border-radius: 4px; padding-top: 10px; /* 给徽章留出空间 */ background: #F3F4F6; color: #111827; border: 1px solid #E0E0E0; min-width: 80px; /* 给代码和控件一些最小宽度, this is consistent with the shared rule now */ box-sizing: border-box; } #mj-control-panel .ref-code-text-main { /* sref代码文本样式 */ display: block; text-align: center; font-family: monospace; font-size: 11px; margin-bottom: 2px; /* 与下方控件的间距 */ word-break: break-all; padding: 0 4px; } #mj-control-panel .ref-code-weight-badge { position: absolute; top: -2px; left: -2px; /* 与图片徽章样式统一 */ font-size: 9px; padding: 1px 3px; border-radius: 4px 0 4px 0; background: rgba(79, 70, 229, 0.1); color: #4f46e5; border-bottom: 1px solid #4f46e5; border-right: 1px solid #4f46e5; z-index: 1; } #mj-control-panel .ref-code-delete-btn { /* 删除按钮样式已全局定义,这里可微调 */ /* 与图片删除按钮一致 */ } /* 新增:参考项内部控件容器 */ #mj-control-panel .ref-item-controls { position: absolute; bottom: 2px; /* 放置在容器底部 */ left: 2px; right: 2px; display: flex; gap: 4px; align-items: center; justify-content: space-between; /* 权重输入和开关两端对齐 */ padding: 0 3px; height: 20px; /* 控制容器高度 */ } #mj-control-panel .ref-code-container .ref-item-controls { /* sref代码的控件容器可能需要微调 */ justify-content: space-around; /* 或者根据需要调整 */ } /* 新增:参考项内部权重输入框 (MODIFIED WIDTH) */ #mj-control-panel .ref-item-weight-input { width: 40px; /* MODIFIED: 较小宽度, 原36px */ padding: 1px 3px; font-size: 10px; border-radius: 3px; border: 1px solid #D1D5DB; /* Gray-300 */ box-sizing: border-box; text-align: center; height: 18px; /* 匹配开关高度 */ } /* 新增:参考项内部禁用/启用开关 (基于全局 .toggle-switch 调整) */ #mj-control-panel .ref-item-disable-toggle { width: 30px; /* 更小尺寸 */ height: 16px; border-radius: 8px; margin-top: 0; /* 移除全局开关的 margin-top */ } #mj-control-panel .ref-item-disable-toggle .toggle-dot { width: 12px; height: 12px; top: 2px; left: 2px; } #mj-control-panel .ref-item-disable-toggle.active .toggle-dot { transform: translateX(14px); /* 调整滑动距离 */ } /* 新增:禁用状态的参考项视觉效果 */ #mj-control-panel .disabled-ref-item { opacity: 0.5; filter: grayscale(50%); /* 轻微灰度 */ } #mj-control-panel .disabled-ref-item .ref-image-preview, #mj-control-panel .disabled-ref-item .ref-code-text-main { /* 如果需要,可以进一步改变图片或文本自身的样式 */ } /* 暗黑模式样式 */ #mj-control-panel.dark-mode { background: #2B2D31; color: #DCDDDE; border-color: #202225; } #mj-control-panel.dark-mode .panel-title { color: #DCDDDE; } #mj-control-panel.dark-mode .panel-divider { background: #40444B; } #mj-control-panel.dark-mode .panel-section { background: #202225; } #mj-control-panel.dark-mode .section-title { color: #DCDDDE; } #mj-control-panel.dark-mode label, #mj-control-panel.dark-mode .param-value-display, #mj-control-panel.dark-mode .imagine-label { color: #DCDDDE; } #mj-control-panel.dark-mode input[type="text"], #mj-control-panel.dark-mode input[type="number"], #mj-control-panel.dark-mode textarea { background: #202225; color: #DCDDDE; border-color: #40444B; } /* 暗黑模式下 select 特殊处理 */ #mj-control-panel.dark-mode select { background-color: #202225 !important; color: #DCDDDE !important; border: 1px solid #40444B !important; box-shadow: none !important; -webkit-appearance: none !important; -moz-appearance: none !important; appearance: none !important; background-image: url('data:image/svg+xml;charset=US-ASCII,') !important; /* 其他 background 属性继承自浅色或已在上面设置,确保 padding-right 覆盖 */ padding-right: 2.5em !important; } #mj-control-panel.dark-mode input[type="text"]::placeholder, #mj-control-panel.dark-mode input[type="number"]::placeholder, #mj-control-panel.dark-mode textarea::placeholder { color: #72767d; opacity: 1; } #mj-control-panel.dark-mode input[type="text"].focused, #mj-control-panel.dark-mode input[type="number"].focused, #mj-control-panel.dark-mode textarea.focused, #mj-control-panel.dark-mode select.focused { border-color: #7289DA !important; /* Discord 紫色 */ } #mj-control-panel.dark-mode #prompt-params { background:#202225; } #mj-control-panel.dark-mode .results-section-divider { border-top-color: #40444B; } /* 暗黑模式主题切换 */ #mj-control-panel.dark-mode .theme-trigger-btn { border-color: #2D2F34; background-color: #2B2D31; color: #DCDDDE; } #mj-control-panel.dark-mode .theme-trigger-btn:hover { border-color: #7289DA; } #mj-control-panel.dark-mode .theme-options-menu { background-color: #2B2D31; border-color: #202225; } #mj-control-panel.dark-mode .theme-option-button { color: #DCDDDE; } #mj-control-panel.dark-mode .theme-option-button:hover { background-color: #393c43; } #mj-control-panel.dark-mode .theme-option-button.active { background-color: #404EED; color: white; } #mj-control-panel.dark-mode .ratio-preview-bg { border-color: #40444B; } #mj-control-panel.dark-mode .ratio-box { background: #40444B; color: #DCDDDE; border-color: #70747A; } #mj-control-panel.dark-mode input[type="range"]::-webkit-slider-runnable-track { background: #40444B; } #mj-control-panel.dark-mode input[type="range"]::-moz-range-track { background: #40444B; } #mj-control-panel.dark-mode input[type="range"]::-webkit-slider-thumb { background-color: #7289DA; border-color: #5865F2; } #mj-control-panel.dark-mode input[type="range"]::-moz-range-thumb { background-color: #7289DA; border-color: #5865F2; } #mj-control-panel.dark-mode .btn-group { border-color: #2D2F34; } #mj-control-panel.dark-mode .btn-group button { background: #40444B; color: #DCDDDE; } #mj-control-panel.dark-mode .btn-group button:not(:last-child) { border-right-color: #2D2F34; } #mj-control-panel.dark-mode .btn-group button.active { background: #5865F2; color: white; } #mj-control-panel.dark-mode .toggle-switch { background: #4E4F52; } #mj-control-panel.dark-mode .toggle-switch .toggle-dot { background: #B9BBBE; } #mj-control-panel.dark-mode .toggle-switch.active { background: #5865F2; } #mj-control-panel.dark-mode .toggle-switch.active .toggle-dot { transform: translateX(20px); } /* 全局开关滑动距离 */ #mj-control-panel.dark-mode .action-button { background:#5865F2; color:white; } #mj-control-panel.dark-mode .action-button:hover { background:#4752C4; } #mj-control-panel.dark-mode .action-button-primary { background:#5865F2; color:white; border: 1px solid #5865F2;} /* 确保有边框或调整 */ #mj-control-panel.dark-mode .action-button-primary:hover { background:#4752C4; } #mj-control-panel.dark-mode .action-button-secondary { background:#40444B; color:#DCDDDE; border: 1px solid #2D2F34; } #mj-control-panel.dark-mode .action-button-secondary:hover { background:#4F545C; } #mj-control-panel.dark-mode #size-buttons button { background: #40444B; color: #DCDDDE; border-color: #2D2F34; } #mj-control-panel.dark-mode #size-buttons button.active { background: #5865F2; color: white; border-color: #5865F2; } #mj-control-panel.dark-mode #size-buttons button:hover:not(.active) { background: #4f545c; } #mj-control-panel.dark-mode .ref-image-preview { border-color: #40444B; } #mj-control-panel.dark-mode .ref-image-preview:hover { box-shadow: 0 2px 6px rgba(0,0,0,0.3); } #mj-control-panel.dark-mode .ref-image-error { background: #40444B; color: #aaa; border: 1px solid #555; } #mj-control-panel.dark-mode .ref-weight-badge { background: rgba(0,0,0,0.75); color: #DCDDDE; border-bottom-color: #5865F2; border-right-color: #5865F2; } #mj-control-panel.dark-mode .ref-delete-btn { background: rgba(239, 68, 68, 0.7); } #mj-control-panel.dark-mode .ref-delete-btn:hover { background: rgb(239, 68, 68); } #mj-control-panel.dark-mode .ref-code-container { background: #202225; color: #DCDDDE; border-color: #2D2F34; } #mj-control-panel.dark-mode .ref-code-weight-badge { background: rgba(88, 101, 242, 0.3); color: #B9BBBE; border-bottom-color: #5865F2; border-right-color: #5865F2; } #mj-control-panel.dark-mode .ref-code-delete-btn { background: rgba(239, 68, 68, 0.3); color: #FAA0A0; } #mj-control-panel.dark-mode .ref-code-delete-btn:hover { background: rgba(239, 68, 68, 0.5); } /* 暗黑模式下参考项内部控件 */ #mj-control-panel.dark-mode .ref-item-weight-input { background-color: #2B2D31; /* 更暗的背景 */ border-color: #40444B; /* 与其他输入框一致的边框 */ color: #DCDDDE; } #mj-control-panel.dark-mode .ref-item-disable-toggle { background: #4E4F52; /* 与全局开关一致 */ } #mj-control-panel.dark-mode .ref-item-disable-toggle .toggle-dot { background: #B9BBBE; /* 与全局开关一致 */ } #mj-control-panel.dark-mode .ref-item-disable-toggle.active { background: #5865F2; /* 与全局开关一致 */ } #mj-control-panel.dark-mode .ref-item-disable-toggle.active .toggle-dot { transform: translateX(14px); /* 调整滑动距离 */ } `; document.head.appendChild(styleSheet); } // 初始化函数 function init() { injectStyles(); resetParams(); // 确保 params 对象在创建UI前已初始化 createSettingButton(); createControlPanel(); // 创建面板并绑定事件 // applyCurrentTheme(); // 已在 createControlPanel 内部调用 // updatePromptParams(); // 已在 createControlPanel -> bindControlEvents -> setInitialActiveButtons/ratioSlider.oninput 中间接触发,或者直接在 bindControlEvents 后调用一次 updatePromptParams(); // 确保首次加载时最终参数区正确显示 } // 确保 Discord 加载完毕后再执行初始化 const discordAppMount = document.getElementById('app-mount'); if (discordAppMount) { setTimeout(init, 500); // 如果 app-mount 已存在,稍作延迟后初始化 } else { // 监听 window load 事件作为后备 window.addEventListener('load', () => setTimeout(init, 500), { once: true }); } })();