// ==UserScript== // @name Discord Midjourney 参数面板 // @namespace https://github.com/cwser // @version 0.2.6 // @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 // @downloadURL none // ==/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, 权重} sref: [], // 格式: {url, 权重} oref: [], // 格式: {url, 权重} iref: [], // 这个参数在原始定义中存在,但未在UI或输出中使用,为保持一致性(如果未来计划使用)而保留 directImages: [], // 格式: {url, 权重} // 新增参数 tile: false, seed: '', quality: 1, // 默认质量为 1 stop: 100, visibility: '', // 新增个性化参数 personalParams: '', // 新增批量任务参数 r: 1 }; let isDarkMode = false; // Moved isDarkMode to a higher scope // 定义 weightPrefixes 映射 const weightPrefixes = { 'cref': '--cw', 'sref': '--sw', 'oref': '--ow', 'directImages': '--iw' }; // 显示 Toast 提示 function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: rgba(0,0,0,0.8); color: white; padding: 10px 16px; border-radius: 6px; z-index: 99999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transform: translateY(-20px); opacity: 0; transition: all 0.3s ease; `; document.body.appendChild(toast); // 触发动画 setTimeout(() => { toast.style.transform = 'translateY(0)'; toast.style.opacity = '1'; }, 10); // 自动消失 setTimeout(() => { toast.style.transform = 'translateY(-20px)'; toast.style.opacity = '0'; setTimeout(() => { if (document.body.contains(toast)) { // 检查 toast 是否仍在 body 中 document.body.removeChild(toast); } }, 300); }, 2000); } // 创建设置按钮 function createSettingButton() { const button = document.createElement('button'); button.textContent = 'MJ设置'; button.style.cssText = ` position: fixed; right: 20px; bottom: 20px; padding: 10px 20px; background-color: #5865F2; color: white; border: none; border-radius: 8px; cursor: pointer; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; `; button.addEventListener('click', toggleControlPanel); document.body.appendChild(button); button.addEventListener('mouseenter', () => { button.style.backgroundColor = '#4752C4'; button.style.transform = 'scale(1.05)'; }); button.addEventListener('mouseleave', () => { button.style.backgroundColor = '#5865F2'; button.style.transform = 'scale(1)'; }); } // 切换控制面板显示/隐藏 function toggleControlPanel() { const panel = document.getElementById('mj-control-panel'); if (panel) { if (panel.style.display === 'none') { panel.style.display = 'block'; panel.style.opacity = '0'; panel.style.transform = 'translateY(10px)'; setTimeout(() => { panel.style.opacity = '1'; panel.style.transform = 'translateY(0)'; }, 10); } else { panel.style.opacity = '0'; panel.style.transform = 'translateY(10px)'; setTimeout(() => { panel.style.display = 'none'; }, 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, // 默认质量重置为 1 stop: 100, visibility: '', personalParams: '', r: 1 }; } // 更新提示词参数 function updatePromptParams() { const { prompt, ar, stylize, weird, chaos, mode, draft, noPrompt, version, cref, sref, speed, oref, /* iref 未在输出中使用 */ directImages, tile, seed, quality, stop, visibility, personalParams } = params; // 处理其他参数 const otherParts = [ `--ar ${ar}`, `--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 !== 'relax' ? `--${speed}` : '', tile ? '--tile' : '', seed ? `--seed ${seed}` : '', quality !== 1 ? `--q ${quality}` : '', // 仅在不是默认值 1 时添加 stop !== 100 ? `--stop ${stop}` : '', visibility ? `--${visibility}` : '', personalParams ? `--p ${personalParams}` : '', params.r > 1 ? `--r ${params.r}` : '' ]; const formatImageWithWeight = (url, weight, prefix) => { let formatted = url; if (weight && weight.trim() !== '') { formatted += ` ${prefix} ${weight.trim()}`; } return formatted; }; const directImageUrls = directImages.map(item => formatImageWithWeight(item.url, item.weight, '--iw')).join(' '); const promptField = document.getElementById('prompt-params'); if (promptField) { const allParts = [ directImageUrls, prompt.trim(), ...cref.map(item => `--cref ${formatImageWithWeight(item.url, item.weight, '--cw')}`), ...sref.map(item => `--sref ${formatImageWithWeight(item.url, item.weight, '--sw')}`), ...oref.map(item => `--oref ${formatImageWithWeight(item.url, item.weight, '--ow')}`), ...otherParts.filter(Boolean) ].filter(Boolean); promptField.value = allParts.join(' ').trim(); } } // 创建控制面板 function createControlPanel() { const panel = document.createElement('div'); panel.id = 'mj-control-panel'; panel.style.cssText = ` display: none; position: fixed; right: 20px; bottom: 80px; /* 已调整,为设置按钮留出空间 */ width: 1080px; max-width: calc(100% - 40px); background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); padding: 20px; z-index: 10000; border: 1px solid #E5E7EB; max-height: 90vh; overflow-y: auto; font-family: sans-serif; transition: opacity 0.2s ease, transform 0.2s ease; /* 为不透明度和变换添加了过渡效果 */ color: #111827; `; panel.innerHTML = `

Midjourney 参数设置

图片尺寸 (--ar)

1:1

美学参数

100
0
0

模型设置

角色参考 (--cref)

风格参考 (--sref)

全方位参考 (--oref)

图像参考 (URL --iw)

更多参数

1
100
1

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

提示词

排除词 (--no)

最终参数

`; document.body.appendChild(panel); bindControlEvents(); // 添加到DOM后绑定事件 } 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(`.${group.className}`); let currentParamValue = params[group.param] === undefined ? group.defaultValue : params[group.param]; buttons.forEach(btn => { if (btn.dataset.value === currentParamValue) { btn.classList.add('active'); btn.style.backgroundColor = isDarkMode ? '#5865F2' : '#4f46e5'; // Adjusted for dark mode btn.style.color = 'white'; } else { btn.classList.remove('active'); btn.style.backgroundColor = isDarkMode ? '#40444B' : 'white'; // Adjusted for dark mode btn.style.color = isDarkMode ? '#DCDDDE' : '#111827'; // Adjusted for dark mode } }); }); } // NEW function: Update visuals for toggle switches function updateToggleVisuals(switchId, property) { const switchEl = document.getElementById(switchId); if (!switchEl) return; const dot = switchEl.querySelector('.toggle-dot'); if (!dot) return; if (params[property]) { dot.style.transform = 'translateX(20px)'; switchEl.style.backgroundColor = isDarkMode ? '#5865F2' : '#4f46e5'; } else { dot.style.transform = 'translateX(0)'; // Use a slightly different off-color for dark mode if desired, or keep consistent switchEl.style.backgroundColor = isDarkMode ? '#4E4F52' : '#e5e7eb'; } } // 修改 setupRefSection 函数 const setupRefSection = (idPrefix, paramKey) => { // idPrefix 用于HTML ID, paramKey 用于 params 对象 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) { // console.error(`Setup failed for ${idPrefix}: missing elements. Make sure IDs in HTML match: ${idPrefix}-add, ${idPrefix}-url, ${idPrefix}-weight`); 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("请输入有效图片URL, 'random'或数字sref码"); return; } const targetArray = params[paramKey]; // 使用 paramKey 访问 params if (!Array.isArray(targetArray)) { // console.error(`params.${paramKey} is not an array.`); return; } const itemUrl = (paramKey === 'sref' && isSrefCode) ? (url.toLowerCase() === 'random' ? 'random' : url) : url; if (!targetArray.some(item => item.url === itemUrl)) { targetArray.push({ url: itemUrl, weight }); const currentWeightPrefix = weightPrefixes[paramKey]; // 使用 paramKey 获取 weightPrefix if (paramKey === 'sref' && isSrefCode) { // 明确检查 paramKey 和 isSrefCode addPreviewSrefCode(previewContainerId, itemUrl, weight); } else { addPreviewImage(previewContainerId, itemUrl, weight, paramKey, currentWeightPrefix); // 传递 paramKey } urlInput.value = ''; weightInput.value = ''; updatePromptParams(); } else { showToast(`该${(paramKey === 'sref' && isSrefCode) ? '码' : 'URL'}已添加`); } }; }; // 绑定控制面板事件 function bindControlEvents() { const $ = id => document.getElementById(id); // isDarkMode is now at a higher scope const themeToggleButton = $('theme-toggle'); const sunIcon = ``; const moonIcon = ``; themeToggleButton.addEventListener('click', () => { isDarkMode = !isDarkMode; const panel = $('mj-control-panel'); const allButtons = panel.querySelectorAll('button'); const inputsAndTextareas = panel.querySelectorAll('input, textarea, select'); const bgElements = panel.querySelectorAll('[style*="background:#f9fafb"], .btn-group'); const labels = panel.querySelectorAll('label, p, h3, span'); if (isDarkMode) { panel.style.backgroundColor = '#2B2D31'; panel.style.color = '#DCDDDE'; panel.style.borderColor = '#202225'; labels.forEach(label => label.style.color = '#DCDDDE'); themeToggleButton.innerHTML = sunIcon; themeToggleButton.style.color = '#DCDDDE'; allButtons.forEach(btn => { if (!btn.classList.contains('active') && !btn.id.startsWith('theme-toggle')) { btn.style.backgroundColor = '#40444B'; btn.style.color = '#DCDDDE'; btn.style.borderColor = '#2D2F34'; } else if (btn.classList.contains('active')) { btn.style.backgroundColor = '#5865F2'; btn.style.color = 'white'; } }); inputsAndTextareas.forEach(input => { input.style.backgroundColor = '#202225'; input.style.color = '#DCDDDE'; input.style.borderColor = '#18191C'; if (input.type === 'range') { input.style.setProperty('--thumb-bg', '#DCDDDE'); input.style.setProperty('--track-bg', '#40444B'); } }); bgElements.forEach(el => el.style.backgroundColor = '#202225'); const ratioBox = $('ratio-box'); if (ratioBox) { ratioBox.style.background = '#40444B'; ratioBox.style.color = '#DCDDDE'; ratioBox.style.borderColor = '#DCDDDE'; } } else { // 亮色模式 panel.style.backgroundColor = 'white'; panel.style.color = '#111827'; panel.style.borderColor = '#E5E7EB'; labels.forEach(label => label.style.color = '#111827'); themeToggleButton.innerHTML = moonIcon; themeToggleButton.style.color = '#6B7280'; allButtons.forEach(btn => { if (!btn.classList.contains('active') && !btn.id.startsWith('theme-toggle')) { btn.style.backgroundColor = 'white'; btn.style.color = '#111827'; btn.style.borderColor = '#d1d5db'; } else if (btn.classList.contains('active')) { btn.style.backgroundColor = '#4f46e5'; btn.style.color = 'white'; } }); inputsAndTextareas.forEach(input => { input.style.backgroundColor = 'white'; input.style.color = '#111827'; input.style.borderColor = '#d1d5db'; if (input.type === 'range') { input.style.removeProperty('--thumb-bg'); input.style.removeProperty('--track-bg'); } }); bgElements.forEach(el => { if(el.classList.contains('btn-group')) { el.style.backgroundColor = 'transparent'; } else { el.style.backgroundColor = '#f9fafb'; } }); const ratioBox = $('ratio-box'); if (ratioBox) { ratioBox.style.background = '#f3f4f6'; ratioBox.style.color = '#374151'; ratioBox.style.borderColor = '#374151'; } } setInitialActiveButtons(); // Refresh button active states according to theme updateToggleVisuals('tile-toggle-switch', 'tile'); // Update toggle visuals for theme updateToggleVisuals('draft-toggle-switch', 'draft'); // Update toggle visuals for theme refreshPreviews(); // To update sref code block colors if needed }); $('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; qualitySlider.value = qualityMap.indexOf(1); 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, defaultValue) => { document.querySelectorAll(`.${groupClass}`).forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll(`.${groupClass}`).forEach(b => { b.classList.remove('active'); b.style.backgroundColor = isDarkMode ? '#40444B' : 'white'; b.style.color = isDarkMode ? '#DCDDDE' : '#111827'; }); btn.classList.add('active'); btn.style.backgroundColor = isDarkMode ? '#5865F2' : '#4f46e5'; btn.style.color = 'white'; params[property] = btn.dataset.value; updatePromptParams(); }); }); }; bindRadioGroup('speed-btn', 'speed', 'relax'); bindRadioGroup('mode-btn', 'mode', 'standard'); bindRadioGroup('visibility-btn', 'visibility', ''); // MODIFIED bindToggleSwitch: Only binds event, visual update is separate const bindToggleSwitchEvent = (switchId, property) => { const switchEl = $(switchId); if (!switchEl) return; switchEl.addEventListener('click', () => { params[property] = !params[property]; updateToggleVisuals(switchId, property); // Update visuals using the new function updatePromptParams(); }); }; bindToggleSwitchEvent('tile-toggle-switch', 'tile'); bindToggleSwitchEvent('draft-toggle-switch', 'draft'); // Set initial visual state for toggles updateToggleVisuals('tile-toggle-switch', 'tile'); updateToggleVisuals('draft-toggle-switch', 'draft'); 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) return; textarea.select(); try { document.execCommand('copy'); showToast('参数已复制!'); } catch (err) { showToast('复制失败!'); console.error('Copy failed:', err); } }; } 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(); // params.draft and params.tile will be false 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; $('ratio-slider').dispatchEvent(new Event('input')); } ['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') && qualityMap) { const resetSliderIndex = qualityMap.indexOf(params.quality); $('quality-slider').value = resetSliderIndex !== -1 ? resetSliderIndex : qualityMap.indexOf(1); } if ($('quality-value')) { $('quality-value').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; // DO NOT re-bind. Instead, update their visuals. updateToggleVisuals('tile-toggle-switch', 'tile'); updateToggleVisuals('draft-toggle-switch', 'draft'); setInitialActiveButtons(); refreshPreviews(); updatePromptParams(); showToast('所有参数已重置为默认值'); }; } // Updated calls to setupRefSection setupRefSection('cref', 'cref'); setupRefSection('sref', 'sref'); setupRefSection('oref', 'oref'); setupRefSection('direct-image', 'directImages'); // idPrefix is 'direct-image', paramKey is '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; } const presetMap = { '纵向': '1:2', '正方形': '1:1', '横向': '2:1' }; document.querySelectorAll('#size-buttons button').forEach(btn => { const btnRatio = presetMap[btn.textContent]; if (btnRatio === ratio) { btn.classList.add('active'); btn.style.backgroundColor = isDarkMode ? '#5865F2' :'#4f46e5'; btn.style.color = 'white'; } else { btn.classList.remove('active'); btn.style.backgroundColor = isDarkMode ? '#40444B' : 'white'; btn.style.color = isDarkMode ? '#DCDDDE' : '#111827'; } }); updatePromptParams(); }; const initialRatioIndex = sizeMap.indexOf(params.ar); ratioSlider.value = initialRatioIndex !== -1 ? initialRatioIndex : 5; ratioSlider.dispatchEvent(new Event('input')); } const btnGroup = $('size-buttons'); if (btnGroup) { const presetMap = { '纵向': '1:2', '正方形': '1:1', '横向': '2:1' }; ['纵向', '正方形', '横向'].forEach((label, i) => { const btn = document.createElement('button'); btn.textContent = label; btn.dataset.value = presetMap[label]; btn.style.cssText = 'padding:4px 12px; border-radius:6px; border:1px solid #d1d5db; background:white; cursor:pointer; transition:all 0.2s ease;'; if ((isDarkMode && params.ar === presetMap[label]) || (!isDarkMode && params.ar === presetMap[label]) ) { btn.classList.add('active'); btn.style.backgroundColor = isDarkMode ? '#5865F2' : '#4f46e5'; btn.style.color = 'white'; } else if (params.ar === presetMap[label]) { // This condition might be redundant now due to above btn.classList.add('active'); btn.style.backgroundColor = '#4f46e5'; btn.style.color = 'white'; } btn.onclick = () => { const ratio = presetMap[label]; const sliderIndex = sizeMap.indexOf(ratio); if (sliderIndex !== -1 && ratioSlider) { ratioSlider.value = sliderIndex; ratioSlider.dispatchEvent(new Event('input')); } }; btnGroup.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; } 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', () => { promptParamsTextarea.select(); try { document.execCommand('copy'); showToast('参数已复制!'); } catch (err) { showToast('复制失败!'); console.error('Copy failed:', err); } }); } document.querySelectorAll('input, textarea, select').forEach(el => { el.addEventListener('focus', () => { el.style.borderColor = isDarkMode ? '#7289DA' : '#4f46e5'; }); el.addEventListener('blur', () => { el.style.borderColor = isDarkMode ? '#202225' : '#d1d5db'; }); }); document.querySelectorAll('button').forEach(btn => { const originalBg = btn.style.backgroundColor; const originalTransform = btn.style.transform; const originalShadow = btn.style.boxShadow; btn.addEventListener('mouseenter', () => { if (!btn.classList.contains('active') && !btn.id.startsWith('theme-toggle')) { btn.style.transform = 'translateY(-1px)'; btn.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; if (isDarkMode) btn.style.backgroundColor = '#4F545C'; // Darken non-active buttons on hover // else it will use CSS hover or remain its light mode default } }); btn.addEventListener('mouseleave', () => { if (!btn.classList.contains('active') && !btn.id.startsWith('theme-toggle')) { // Added check for theme toggle btn.style.transform = originalTransform || 'translateY(0)'; btn.style.boxShadow = originalShadow || 'none'; if (isDarkMode) btn.style.backgroundColor = '#40444B'; // Restore dark non-active button bg // else it will revert to its original light mode color or CSS default } }); }); } function addPreviewImage(containerId, url, weight, paramKey, currentWeightPrefix) { // paramKey is used for deletion const container = document.getElementById(containerId); if (!container) return; const imgContainer = document.createElement('div'); imgContainer.style.cssText = 'position: relative; margin: 4px; animation: fadeIn 0.3s ease;'; const img = document.createElement('img'); img.src = url; img.style.width = '60px'; img.style.height = '60px'; img.style.objectFit = 'cover'; img.style.borderRadius = '4px'; img.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; img.style.transition = 'all 0.2s ease'; img.onerror = () => { imgContainer.innerHTML = `
无效图片
`; if(isDarkMode) { const errorDiv = imgContainer.firstChild; errorDiv.style.background = '#40444B'; errorDiv.style.color = '#aaa'; } }; img.addEventListener('mouseenter', () => { img.style.transform = 'scale(1.05)'; img.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)'; }); img.addEventListener('mouseleave', () => { img.style.transform = 'scale(1)'; img.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; }); const weightBadge = document.createElement('div'); weightBadge.style.cssText = 'position: absolute; bottom: 0; left: 0; background: rgba(0,0,0,0.7); color: white; font-size: 10px; padding: 1px 3px; border-radius: 0 4px 0 0;'; const wP = currentWeightPrefix ? currentWeightPrefix.replace('--', '') : ''; weightBadge.textContent = weight && weight.trim() !== '' ? `${wP}:${weight}` : `${wP}:默认`; const deleteBtn = document.createElement('button'); deleteBtn.style.cssText = 'position: absolute; top: -4px; right: -4px; background: rgba(239, 68, 68, 0.9); color: white; border: none; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; line-height: 16px; text-align:center; cursor:pointer; transition:all 0.2s ease; opacity: 0.8; display:flex; align-items:center; justify-content:center;'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function(e) { e.stopPropagation(); imgContainer.style.animation = 'fadeOut 0.2s ease forwards'; setTimeout(() => { const targetArray = params[paramKey]; // Use paramKey to find the correct array in params if (Array.isArray(targetArray)) { const index = targetArray.findIndex(item => item.url === url); if (index !== -1) { targetArray.splice(index, 1); if (container.contains(imgContainer)) container.removeChild(imgContainer); updatePromptParams(); } } }, 200); }; deleteBtn.addEventListener('mouseenter', () => { deleteBtn.style.opacity = '1'; deleteBtn.style.transform = 'scale(1.1)'; }); deleteBtn.addEventListener('mouseleave', () => { deleteBtn.style.opacity = '0.8'; deleteBtn.style.transform = 'scale(1)'; }); imgContainer.appendChild(img); imgContainer.appendChild(weightBadge); imgContainer.appendChild(deleteBtn); container.appendChild(imgContainer); } function addPreviewSrefCode(containerId, code, weight) { const container = document.getElementById(containerId); if (!container) return; const codeContainer = document.createElement('div'); codeContainer.style.cssText = 'position: relative; margin: 4px; background: #f3f4f6; border-radius: 4px; padding: 6px 10px; display: flex; align-items: center; font-family: monospace; font-size: 12px; color: #111827; animation: fadeIn 0.3s ease;'; if (isDarkMode) { // Adjusted for dark mode codeContainer.style.background = '#202225'; codeContainer.style.color = '#DCDDDE'; } const codeText = document.createElement('span'); codeText.textContent = `sref:${code}`; const weightBadge = document.createElement('div'); weightBadge.style.cssText = 'margin-left: 8px; background: rgba(79, 70, 229, 0.1); color: #4f46e5; font-size: 10px; padding: 2px 4px; border-radius: 3px;'; weightBadge.textContent = weight && weight.trim() !== '' ? `sw:${weight}` : 'sw:默认'; if (isDarkMode) { // Adjusted for dark mode weightBadge.style.background = 'rgba(88, 101, 242, 0.3)'; weightBadge.style.color = '#B9BBBE'; } const deleteBtn = document.createElement('button'); deleteBtn.style.cssText = 'margin-left: 10px; background: rgba(239, 68, 68, 0.1); color: #ef4444; border: none; border-radius: 4px; padding: 2px 5px; font-size: 10px; line-height:1; cursor:pointer; transition:all 0.2s ease;'; if (isDarkMode) { // Adjusted for dark mode deleteBtn.style.background = 'rgba(239, 68, 68, 0.3)'; deleteBtn.style.color = '#FAA'; } deleteBtn.innerHTML = '×'; deleteBtn.onclick = function(e) { e.stopPropagation(); codeContainer.style.animation = 'fadeOut 0.2s ease forwards'; setTimeout(() => { const index = params.sref.findIndex(item => item.url === code); // sref is specific here if (index !== -1) { params.sref.splice(index, 1); if (container.contains(codeContainer)) container.removeChild(codeContainer); updatePromptParams(); } }, 200); }; // Hover effect for delete button, adjusted for dark mode const originalDeleteBg = isDarkMode ? 'rgba(239, 68, 68, 0.3)' : 'rgba(239, 68, 68, 0.1)'; const hoverDeleteBg = isDarkMode ? 'rgba(239, 68, 68, 0.4)' : 'rgba(239, 68, 68, 0.2)'; deleteBtn.addEventListener('mouseenter', () => { deleteBtn.style.backgroundColor = hoverDeleteBg; }); deleteBtn.addEventListener('mouseleave', () => { deleteBtn.style.backgroundColor = originalDeleteBg; }); codeContainer.appendChild(codeText); codeContainer.appendChild(weightBadge); codeContainer.appendChild(deleteBtn); container.appendChild(codeContainer); } function refreshPreviews() { // paramKey here corresponds to the key in the params object and weightPrefixes map 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 => { const currentWeightPrefix = weightPrefixes[ref.paramKey]; if (ref.paramKey === 'sref' && (item.url === 'random' || /^\d+$/.test(item.url))) { addPreviewSrefCode(ref.previewId, item.url, item.weight); } else { addPreviewImage(ref.previewId, item.url, item.weight, ref.paramKey, currentWeightPrefix); } }); } }); } 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); } } input[type="range"] { -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; width: 100%;} input[type="range"]::-webkit-slider-runnable-track { background: #e5e7eb; height: 0.4rem; border-radius: 0.2rem; } input[type="range"]::-moz-range-track { background: #e5e7eb; height: 0.4rem; border-radius: 0.2rem; } 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;} input[type="range"]::-moz-range-thumb { border: none; border-radius: 50%; background-color: #4f46e5; height: 1rem; width: 1rem; border: 1px solid #3730a3;} /* Dark mode styles for range inputs */ html[data-theme='dark'] input[type="range"]::-webkit-slider-runnable-track, body[style*="background-color: rgb(43, 45, 49)"] input[type="range"]::-webkit-slider-runnable-track { /* A bit more specific for Discord's dark */ background: #40444B; /* var(--track-bg) */ } html[data-theme='dark'] input[type="range"]::-moz-range-track, body[style*="background-color: rgb(43, 45, 49)"] input[type="range"]::-moz-range-track { background: #40444B; /* var(--track-bg) */ } html[data-theme='dark'] input[type="range"]::-webkit-slider-thumb, body[style*="background-color: rgb(43, 45, 49)"] input[type="range"]::-webkit-slider-thumb { background-color: #DCDDDE; /* var(--thumb-bg) */ border-color: #B9BBBE; } html[data-theme='dark'] input[type="range"]::-moz-range-thumb, body[style*="background-color: rgb(43, 45, 49)"] input[type="range"]::-moz-range-thumb { background-color: #DCDDDE; /* var(--thumb-bg) */ border-color: #B9BBBE; } `; document.head.appendChild(styleSheet); // Check if Font Awesome is already loaded to avoid duplicates if other scripts use it. if (!document.querySelector('link[href*="font-awesome"]')) { const faLink = document.createElement('link'); faLink.rel = 'stylesheet'; faLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css'; // Consider a specific version or SRI document.head.appendChild(faLink); } } function init() { injectStyles(); resetParams(); // Initialize params with defaults createSettingButton(); createControlPanel(); // This calls bindControlEvents internally setInitialActiveButtons(); // Set active states for radio-like buttons updatePromptParams(); // Populate the final prompt field initially } const discordAppMount = document.getElementById('app-mount'); if (discordAppMount) { init(); } else { window.addEventListener('load', init, { once: true }); } })();