Greasy Fork

Greasy Fork is available in English.

即梦输入框优化

调整即梦AI绘图页面的提示词输入框高度,并添加格式化按钮去除特殊字符。

// ==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
// ==/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);
        }
    }

})();