// ==UserScript== // @name 即梦输入框优化 // @namespace http://tampermonkey.net/ // @version 0.5 // @description 调整即梦AI绘图页面的提示词输入框高度,并添加格式化按钮去除特殊字符。 // @author wlct // @match https://jimeng.jianying.com/ai-tool/image/generate // @grant GM_addStyle // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/533565/%E5%8D%B3%E6%A2%A6%E8%BE%93%E5%85%A5%E6%A1%86%E4%BC%98%E5%8C%96.user.js // @updateURL https://update.greasyfork.icu/scripts/533565/%E5%8D%B3%E6%A2%A6%E8%BE%93%E5%85%A5%E6%A1%86%E4%BC%98%E5%8C%96.meta.js // ==/UserScript== (function() { 'use strict'; // --- 配置 --- const INPUT_SELECTOR = 'div[contenteditable="true"][role="textbox"].tiptap.ProseMirror'; // 提示词输入框的选择器 const INPUT_CONTAINER_SELECTOR = '.tiptap-container'; // 输入框外层容器 const TARGET_HEIGHT = '280px'; // 目标高度 const BUTTON_TEXT = '格式化提示词'; // 按钮文字 const BUTTON_ID = 'jimeng-format-prompt-button'; // 按钮 ID const POLLING_INTERVAL = 500; // 轮询间隔 (毫秒) const TIMEOUT_DURATION = 15000; // 超时时间 (毫秒) // --- 状态变量 --- let intervalId = null; let timeoutId = null; let isInitialized = false; // --- 主要逻辑 --- function init() { // 如果已经初始化,则不重复执行 if (window.__jimengOptimizerInitialized) { console.log('即梦优化脚本:已经初始化,跳过...'); return; } window.__jimengOptimizerInitialized = true; console.log('即梦优化脚本:开始查找元素...'); // 清除之前的定时器(以防万一) if (intervalId) clearInterval(intervalId); if (timeoutId) clearTimeout(timeoutId); // 使用 GM_addStyle 添加全局样式 - 结合 11.js 中成功的选择器和样式 addGlobalStyle(); intervalId = setInterval(() => { if (isInitialized) { clearInterval(intervalId); clearTimeout(timeoutId); return; } const inputElement = document.querySelector(INPUT_SELECTOR); if (inputElement) { console.log('即梦优化脚本:找到输入框元素!'); isInitialized = true; clearInterval(intervalId); clearTimeout(timeoutId); // --- 创建并添加按钮 --- createAndAppendButton(inputElement); // --------------------- } }, POLLING_INTERVAL); // 设置超时,如果在 TIMEOUT_DURATION 后仍未找到元素,则停止轮询 timeoutId = setTimeout(() => { if (!isInitialized) { clearInterval(intervalId); console.warn('即梦优化脚本:超时!未能找到输入框元素。'); } }, TIMEOUT_DURATION); } // --- 添加全局样式 (使用 GM_addStyle) --- function addGlobalStyle() { // 直接使用 11.js 中的有效选择器和样式,同时保留我们的样式 GM_addStyle(` /* 从 11.js 借鉴的样式 */ #image-input-drag-content { height: auto !important; min-height: ${TARGET_HEIGHT} !important; } /* 尝试更多可能的选择器 */ .tiptap-editor, .tiptap-container, .editor-container, .editor-wrapper, div[role="textbox"], div[data-slate-editor], div.tiptap { min-height: ${TARGET_HEIGHT} !important; height: auto !important; max-height: none !important; } /* 输入框本身 */ ${INPUT_SELECTOR} { min-height: ${TARGET_HEIGHT} !important; height: auto !important; } /* 修复可能的父容器限制 */ .prompt-wrapper, .input-container, .prompt-container, .textbox-container { min-height: ${TARGET_HEIGHT} !important; height: auto !important; max-height: none !important; } /* 按钮样式 */ #${BUTTON_ID} { display: inline-block !important; margin: 10px 0 !important; padding: 8px 15px !important; cursor: pointer !important; border: 1px solid #ccc !important; border-radius: 4px !important; background-color: #f0f0f0 !important; font-size: 14px !important; z-index: 9999 !important; position: relative !important; } #${BUTTON_ID}:hover { background-color: #e0e0e0 !important; } `); console.log('即梦优化脚本:全局样式已通过 GM_addStyle 添加'); } // --- 启动脚本 --- // 尝试在 DOM 加载后直接运行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); // 如果 DOM 已经加载,直接运行 } function createAndAppendButton(inputElement) { if (!inputElement || document.getElementById(BUTTON_ID)) { // 如果输入框不存在或按钮已存在,则不执行 return; } console.log('即梦优化脚本:创建格式化按钮...'); const button = document.createElement('button'); button.id = BUTTON_ID; button.textContent = BUTTON_TEXT; // 尝试找到各种可能的容器,按优先级排序 const containers = [ document.querySelector(INPUT_CONTAINER_SELECTOR), // 我们原来的容器选择器 inputElement.closest('#image-input-drag-content'), // 从11.js借鉴的选择器 inputElement.closest('.tiptap-editor'), inputElement.closest('.editor-container'), inputElement.closest('.prompt-wrapper'), inputElement.parentElement, inputElement.parentElement?.parentElement ]; // 找到第一个有效的容器 const container = containers.find(c => c !== null && c !== undefined); if (container && container.parentNode) { // 创建按钮容器,并设置样式 const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'margin: 15px 0; text-align: left; width: 100%; clear: both; position: relative; z-index: 1000;'; buttonContainer.appendChild(button); // 将按钮容器插入到输入框容器后面 container.parentNode.insertBefore(buttonContainer, container.nextSibling); console.log('即梦优化脚本:按钮已添加到输入框容器后面。'); } else { // 备用方案:添加到body,并使用绝对定位 console.warn('即梦优化脚本:未能找到预期的容器,尝试备用方案添加按钮。'); const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'margin: 15px 0; text-align: left; clear: both; position: absolute; z-index: 9999;'; buttonContainer.appendChild(button); document.body.appendChild(buttonContainer); const rect = inputElement.getBoundingClientRect(); buttonContainer.style.top = (window.scrollY + rect.bottom + 10) + 'px'; buttonContainer.style.left = (window.scrollX + rect.left) + 'px'; buttonContainer.style.width = 'auto'; console.log('即梦优化脚本:按钮已添加到绝对位置。'); } // 绑定点击事件 button.addEventListener('click', function() { console.log('即梦优化脚本:格式化按钮被点击'); try { formatPrompt(inputElement); } catch (error) { console.error('即梦优化脚本:格式化过程出错', error); backupFormatMethod(inputElement); } }); } // 备用格式化方法 function backupFormatMethod(element) { if (!element) return; console.log('即梦优化脚本:尝试备用格式化方法...'); try { const originalText = element.textContent || ''; // 移除所有不可见字符和特殊控制字符 const formattedText = cleanText(originalText); if (originalText !== formattedText) { element.textContent = formattedText; // 尝试触发多种输入相关事件以确保网站检测到变化 triggerInputEvents(element); console.log('即梦优化脚本:使用备用方法格式化完成'); showSuccess(); } else { console.log('即梦优化脚本:无需格式化'); } } catch (error) { console.error('即梦优化脚本:备用格式化方法失败', error); } } // 主要格式化方法 function formatPrompt(element) { if (element) { console.log('即梦优化脚本:格式化提示词...'); let originalText; try { originalText = element.innerText || ''; } catch (e) { originalText = element.textContent || ''; console.log('即梦优化脚本:使用textContent替代innerText'); } // 清理文本,移除所有不可见字符和特殊控制字符 const formattedText = cleanText(originalText); if (originalText !== formattedText) { try { // 尝试使用不同方法设置内容 if (typeof element.innerText !== 'undefined') { element.innerText = formattedText; } else { element.textContent = formattedText; console.log('即梦优化脚本:使用textContent设置内容'); } // 尝试触发多种输入相关事件 triggerInputEvents(element); console.log('即梦优化脚本:提示词已格式化。'); showSuccess(); } catch (e) { console.error('即梦优化脚本:设置内容失败', e); // 最后尝试直接设置innerHTML try { element.innerHTML = formattedText; triggerInputEvents(element); } catch (error) { console.error('即梦优化脚本:所有设置方法都失败', error); } } } else { console.log('即梦优化脚本:提示词无需格式化。'); } } } // 清理文本,移除所有不可见字符和特殊控制字符 function cleanText(text) { if (!text) return ''; let formattedText = text // 移除零宽连接符 .replace(/\u200C/g, '') // 移除零宽空格 .replace(/\u200B/g, '') // 移除零宽非连接符 .replace(/\u200D/g, '') // 移除零宽不换行空格 .replace(/\uFEFF/g, '') // 移除各种控制字符 .replace(/[\u0000-\u001F\u007F-\u009F]/g, '') // 移除各种特殊空格,但保留普通空格 .replace(/[\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/g, ' ') // 移除Unicode组合标记 .replace(/[\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/g, '') // 删除英文破折号(en dash、em dash) .replace(/[–—]/g, ''); // 不再移除任何标点符号和特殊字符,只将单引号替换为双引号 // 将所有类型的单引号替换成双引号 formattedText = formattedText.replace(/['']/g, '"'); console.log('即梦优化脚本:文本清理完成'); return formattedText; } // 尝试触发多种输入相关事件以确保网站检测到变化 function triggerInputEvents(element) { const events = ['input', 'change', 'keyup', 'keydown', 'keypress']; events.forEach(eventType => { try { const event = new Event(eventType, { bubbles: true }); element.dispatchEvent(event); } catch (e) { console.log(`即梦优化脚本:触发${eventType}事件失败`); } }); // 尝试模拟按键事件 try { const keyEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: ' ', keyCode: 32 }); element.dispatchEvent(keyEvent); } catch (e) { console.log('即梦优化脚本:触发keydown事件失败'); } } // 显示成功提示 function showSuccess() { const btn = document.getElementById(BUTTON_ID); if(btn) { const originalBg = btn.style.backgroundColor || '#f0f0f0'; btn.style.backgroundColor = '#c8e6c9'; setTimeout(() => { btn.style.backgroundColor = originalBg; }, 1000); } } })();