Greasy Fork is available in English.

Coze 布局调整

Coze 聊天栏调整, 修改聊天栏默认位置和默认宽度, 添加收起头栏的控制按钮, 优化 Coze 部分细节.

// ==UserScript==
// @name         Coze 布局调整
// @namespace    http://tampermonkey.net/
// @version      1.7.3
// @description  Coze 聊天栏调整, 修改聊天栏默认位置和默认宽度, 添加收起头栏的控制按钮, 优化 Coze 部分细节.
// @author       太阳照常升起
// @match        https://www.coze.com/*
// @icon         https://sf-coze-web-cdn.coze.com/obj/coze-web-sg/obric/coze/favicon.1970.png
// @grant        none
// @license      MIT
// ==/UserScript==

let chatSwap = true;

function Camel2Kebab(string) {
    return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}

/* 通用 JSON 转 CSS 方法 */
function Json2Css(complexStyleObject) {
    let cssString = '';

    for (const selector in complexStyleObject) {
        if (selector.startsWith('@')) { /* 处理媒体查询和其他@规则 */
            cssString += `${selector} { `;
            for (const innerSelector in complexStyleObject[selector]) {
                cssString += `${innerSelector} { `;
                const styles = complexStyleObject[selector][innerSelector];
                for (const prop in styles) {
                    cssString += `${Camel2Kebab(prop)}: ${styles[prop]}; `;
                }
                cssString += '} ';
            }
            cssString += '} ';
        } else {
            cssString += `${selector} { `;
            const styles = complexStyleObject[selector];
            for (const prop in styles) {
                cssString += `${Camel2Kebab(prop)}: ${styles[prop]}; `;
            }
            cssString += '} ';
        }
    }

    return cssString;
}

const defaultStylesJSON = {
    '.hook-button': {
        fontSize: '0.85rem',
        color: '#567',
        backgroundColor: '#AABBCC20',
        border: '2px solid #ABC',
        padding: '0.35em 0.85em',
        transition: 'all 0.15s ease-out',
        zIndex: '0',
        outline: 'none'
    },
    '.hook-button:hover': {
        color: '#4D53E8',
        border: '2px solid #4D53E8',
        boxShadow: '0px 0px 4px #20304050',
        zIndex: '1024',
        cursor: 'pointer'
    },
    '.left-button': {
        borderRadius: '4px 0 0 4px',
        marginRight: '-2px'
    },
    '.right-button': {
        borderRadius: '0 4px 4px 0',
    },
    '.SpjOpcOJdSoQECybVksF': {
        backgroundColor: '#FFF',
        borderRadius: '8px',
        border: '2px solid #00000014',
        paddingTop: '20px',
        boxShadow: '0px 0px 8px #00000010',
        transition: 'all 0.15s ease-out',
    },
    '.R_WS6aCLs2gN7PUhpDB0 .hv5iKiECwMBS4Ig79KEg .CR2NV8AIey04fyMgZdCy': {
        gap: '16px'
    },
    '.nIVxVV6ZU7gCM5i4VQIL': {
        padding: '16px 32px 32px 32px'
    },
    '.CKOXiJzzJsU73hzE_M4r': {
        borderBottom: '1px solid #00000015'
    },
    '.nIP4BqLGD8csFme4CavI': {
        borderTop: '1px solid #00000015'
    },
    '.gqtYhfRVZuEPPIAbZVxm': {
        display: 'flex',
        flexDirection: 'row',
    }
}

const defaultStyleClass = document.createElement('style');
defaultStyleClass.innerHTML = `${Json2Css(defaultStylesJSON)}`;

/* 增加两秒延时, 确保覆盖原版 css 的代码能有效覆盖 */
setTimeout(() => {
    document.head.appendChild(defaultStyleClass);
}, 2000);

// 监听对话窗口布局
(() => {
    const previewStyles = {
        borderLeft: '1px solid rgba(28, 29, 35, .12)',
    }

    setInterval(() => {
        let mainGrid = document.querySelector('.sidesheet-container.UMf9npeM8cVkDi0CDqZ0');
        if (mainGrid) { mainGrid.style.gridTemplateColumns = '1fr'; }

        // 对话 div
        let previewBox = document.querySelector('.TH9DlQU1qwg_KGXdDYzk');
        setElementStyles(previewBox, previewStyles);

        // 技能 div
        let skillsBox = document.querySelector('.Tu_0qwgY5xgutNNqZdO5.coz-bg-plus');
        if (skillsBox) {
            skillsBox.parentElement.style.transition = 'grid-template-columns 0.15s ease-out';
        }

        let personaPromptBox = document.querySelector('.WOldSMY37_Moixx1AljK.coz-bg-plus.V7YfWLfkPCgVbNaXN15Y.vPzhZr_lrLIQ8pYaTon8.nwXp2fORjEjvPzaqxE8n')
        setElementStyles(personaPromptBox, {
            marginBottom: '0',
            borderBottom: '0',
            paddingBottom: '0',
        });

        if (previewBox && skillsBox) {
            if (skillsBox.previousElementSibling != previewBox) {
                skillsBox.insertAdjacentElement('beforebegin', previewBox);
            }

            skillsBox.parentElement.style.gridTemplateColumns = chatSwap ? '6fr 19fr 6fr' : '1fr 1fr 1fr';
        }
    }, 100);
})();

// 管理头栏
(() => {

    // 关闭头栏按钮
    let expandHeader = '展开标头',
        collapseHeader = '折叠标头',
        headerButton = document.createElement('button');
    headerButton.innerText = collapseHeader;

    // 宽度控制按钮
    let lengthen = '增加宽度',
        reduction = '还原宽度',
        widthButton = document.createElement('button');
    widthButton.innerText = reduction;

    headerButton.className += 'hook-button left-button';
    widthButton.className += 'hook-button right-button';

    widthButton.addEventListener('click', () => {
        chatSwap = !chatSwap;
        widthButton.innerText = chatSwap ? reduction : lengthen;
    });

    const chatInputStyles = {
        marginTop: '1rem',
        marginBottom: '1rem',
    }

    let initialized = false;

    setInterval(() => {
        // 头栏
        let header = document.querySelector('.autboP_xS3EJZt4GoTeY');
        setElementStyles(header, { transition: 'margin-top 0.15s ease-out' });

        let developHeader = document.querySelector('.LxUy6g0wgIgIWCGkQHkC.coz-bg-plus.coz-fg-secondary.Zo84sv5CjcC2ObBGEGDy.SKIazToEhtUg8ZweE2b6');
        setElementStyles(developHeader, { transition: 'margin-top 0.15s ease-out' });

        // Preview 的父元素
        let DevelopBox = document.querySelector('.LxUy6g0wgIgIWCGkQHkC.MWPtuVYYnlMYfpAdNbLa');

        /* .LxUy6g0wgIgIWCGkQHkC.coz-bg-plus.coz-fg-secondary.Zo84sv5CjcC2ObBGEGDy.SKIazToEhtUg8ZweE2b6 */
        // 模型选择栏
        let modelSelectionBar = document.querySelector('.semi-button.semi-button-primary.semi-button-size-small.semi-button-borderless.HTYZGLz47cQtNZFtW3e1.yJDwteqjbdkspQ0V7XOl.hPxLPb8zfs4SdYQh_64B.semi-button-with-icon');
        if (modelSelectionBar) modelSelectionBar.style.marginRight = '1rem';

        // 对话列表渐变遮挡
        let chatListGradient = document.querySelector('.qtV_UKcJKqgw6X0fPvI4');
        chatListGradient && chatListGradient.remove();

        // 对话输入框
        let editBarBox = document.querySelector('.WfXRc6x8M2gbaaX2HSxJ');
        if (editBarBox) {
            setElementStyles(editBarBox, chatInputStyles);
        }

        if (DevelopBox && header && !DevelopBox.contains(headerButton)) {
            DevelopBox.appendChild(modelSelectionBar);
            DevelopBox.appendChild(headerButton);
            DevelopBox.appendChild(widthButton);

            headerButton.addEventListener('click', () => {
                ToggleHeaderStatus(developHeader, header, headerButton, expandHeader, collapseHeader);
            })

            if (!initialized) {
                initialized = true;
                setTimeout(() => {
                    ToggleHeaderStatus(developHeader, header, headerButton, expandHeader, collapseHeader);
                }, 750);
            }
        }
    }, 100);
})();

function ToggleHeaderStatus(developHeader, header, headerButton, expandHeader, collapseHeader) {
    if (header.style.marginTop !== '-74px') {

        developHeader.style.marginTop = '-64px';
        header.style.marginTop = '-74px';
        headerButton.innerText = expandHeader;
    } else {

        developHeader.style.marginTop = '0px';
        header.style.marginTop = '0px';
        headerButton.innerText = collapseHeader;
    }
}

// 删除输入框下面的两个元素
(() => {

    let firstBelowTheEditBar = document.querySelector('.pStAbHgTdAlDVUlpMOGP');
    let secondBelowTheEditBar = document.querySelector('.RFqvgJWAYggvoBpHs2cU._dMuc1A3gGA7leIE6moV');

    setInterval(() => {
        /* 聊天框上面的间隔元素 */
        let xuVS0D35c4AvLVNVLt0t = document.querySelector('.xuVS0D35c4AvLVNVLt0t');
        if (xuVS0D35c4AvLVNVLt0t) {
            xuVS0D35c4AvLVNVLt0t.remove();
        }

        let ZkB2FCWzuVcemZD_RKWd = document.querySelector('.ZkB2FCWzuVcemZD_RKWd');
        if (ZkB2FCWzuVcemZD_RKWd) {
            ZkB2FCWzuVcemZD_RKWd.remove();
        }

        firstBelowTheEditBar = bodyNotContainThenQuery(firstBelowTheEditBar, '.pStAbHgTdAlDVUlpMOGP');
        if (firstBelowTheEditBar) { firstBelowTheEditBar.remove(); }

        secondBelowTheEditBar = bodyNotContainThenQuery(secondBelowTheEditBar, '.RFqvgJWAYggvoBpHs2cU._dMuc1A3gGA7leIE6moV');
        if (secondBelowTheEditBar) { secondBelowTheEditBar.remove(); }
    }, 100);
})();

// 优化编辑栏样式
(() => {
    // 编辑栏
    let editBar = document.querySelector('.k5ePpJvczIMzaNIaOwKS');

    // 额外样式
    const styles = {
        borderWidth: '2px',
        borderRadius: '16px',
        transition: 'border 0.15s ease-out'
    }

    setInterval(() => {

        // 如果页面 [更新] 就需要重新寻找
        editBar = bodyNotContainThenQuery(editBar, '.k5ePpJvczIMzaNIaOwKS');

        if (editBar) {
            setElementStyles(editBar, styles);

            let textarea = editBar.firstElementChild.firstElementChild;
            if (textarea) {
                textarea.placeholder = '请输入你的问题';
            }
        }
    }, 100);
})();


/**
 * @param {Element} element 
 * @param {String} action 
 * @returns {Element|null}
 */
function bodyNotContainThenQuery(element, selectors) {
    if (!document.body.contains(element)) {
        return document.body.querySelector(selectors);
    }
    return element;
}


/**
 * @param {Element} element 目标元素
 * @param {{}} styles 目标样式
 */
function setElementStyles(element, styles) {
    if (!element) return;
    Object.keys(styles).forEach(key => {
        element.style[key] = styles[key];
    });
}