给页面临时设置宽屏

see https://ld246.com/article/1746079460404

// 给页面临时设置宽屏(暂不支持持久,即文档关闭或页面刷新后复原)
// 兼容编辑器宽度插件和思源自适应宽度,兼容标题宽度
// see https://ld246.com/article/1746079460404
// 修改改自 https://ld246.com/article/1744597829665
(()=>{
    // 是否隐藏侧边栏,true 隐藏 false不隐藏
    const isHideDock = true;
    
    // 添加风格
    const styles = {
        "↔️": {tips:'宽屏', style:`width: 100%!important;margin:0 auto;padding-left:20px!important;padding-right:20px!important;.av{font-size: 16px;}`},
    };

    // 鼠标悬停是否显示提示信息,true显示,false不显示
    const showTips = true;
    
    // 手机版返回
    if(isMobile()) return;
    
    // 监听dock加载完毕
    whenElementExist('#dockRight .dock__items .dock__item--pin').then((pin)=>{
        // 这里可以添加多个风格
        Object.entries(styles).forEach(([key, val]) => {
            addStyleButton(key, val, pin);
        });
    });

    // 设置风格按钮
    const styleStatus = {};
    function addStyleButton(key, val, pin) {
        const buttonString = `<span class="dock__item ariaLabel" aria-label="${showTips ? '设置'+val.tips+'风格' : ''}">${key}</span>`;
        // 创建一个 DocumentFragment
        const fragment = document.createRange().createContextualFragment(buttonString);
        // 提取 span 元素
        const button = fragment.firstChild;
        button.onclick = (event) => {
            event.preventDefault(); // 阻止表单提交的默认行为
            event.stopPropagation(); // 阻止事件冒泡
            if(!styleStatus[key]) {
                // 添加样式
                setPageStyle(val.style);
                styleStatus[key] = true;
                button.setAttribute('aria-label', '取消'+val.tips+'风格');
                // 隐藏侧边栏
                toggleDockItems(false);
            } else {
                // 恢复样式
                setPageStyle('');
                styleStatus[key] = false;
                button.setAttribute('aria-label', '设置'+val.tips+'风格');
                // 显示侧边栏
                toggleDockItems(true);
            }
        };
        pin.before(button);
    }

    let lastProtyle = null;
    function setPageStyle(style) {
        const protyle = document.querySelector('[data-type="wnd"].layout__wnd--active .protyle:not(.fn__none)');
        const protyleId = protyle?.dataset?.id;
        if(!protyleId) {
            showMessage('请先选择要设置的编辑器!', true);
        }
        lastProtyle = protyle;
        const width = extractWidth(style);
        // #layouts div.layout__center div.protyle-content:not([data-fullwidth="true"]) div.protyle-wysiwyg
        style = `#layouts div.layout__center .protyle[data-id="${protyleId}"] div.protyle-content div.protyle-wysiwyg{${style}}`;
        if(width) style += `#layouts div.layout__center .protyle[data-id="${protyleId}"] div.protyle-content div.protyle-title{margin-left: max(calc(max(calc((100% - ${width}) / 2), 16px) + 0px), 16px) !important;margin-right: max(calc(max(calc((100% - ${width}) / 2), 16px) - 0px), 16px)!important;}`;
        setStyle(style, protyleId);
    }

    function extractWidth(styleCode) {
        // 1. 过滤所有类选择器规则(包括嵌套)
        const cleanedCode = styleCode.replace(/\.\w+\s*{[^}]*}/g, '');
        // 2. 精确匹配width属性值(兼容空格和特殊符号)
        const regex = /width\s*:\s*([^;}]+)/i;
        const match = cleanedCode.match(regex);
        // 3. 仅移除尾部可能残留的 `}` 符号
        return match ? match[1].trim().replace(/}$/, '').replace(/!important/ig, '').trim() : null;
    }

    function setStyle(css, protyleId) {
        // 1. 移除旧的样式(通过唯一ID)
        const existingStyle = document.getElementById(`protyle-${protyleId}-style`);
        if (existingStyle) {
            existingStyle.remove();
        }
        // 2. 创建新的 <style> 元素
        const style = document.createElement('style');
        style.id = `protyle-${protyleId}-style`; // 唯一标识符
        style.textContent = css; // 设置 CSS 内容
        // 3. 将样式添加到页面头部
        document.head.appendChild(style);
    }

    function showMessage(message, isError = false, delay = 7000) {
        return fetch('/api/notification/' + (isError ? 'pushErrMsg' : 'pushMsg'), {
            "method": "POST",
            "body": JSON.stringify({"msg": message, "timeout": delay})
        });
    }

    function isMobile() {
        return !!document.getElementById("sidebar");
    }
    
    // 等待元素出现
    function whenElementExist(selector, node) {
        return new Promise(resolve => {
            const check = () => {
                const el = typeof selector==='function'?selector():(node||document).querySelector(selector);
                if (el) resolve(el); else requestAnimationFrame(check);
            };
            check();
        });
    }
    // 隐藏/显示侧边栏
    let lastDockTypes = [];
    function toggleDockItems(show) {
        const activeDocks = document.querySelectorAll('.dock__item--active');
        if(show === true) {
            // 显示侧边栏
            if (lastDockTypes.length > 0) {
                const dockTypes = Array.from(activeDocks).map(dock => `[data-type="${dock.dataset.type}"]`);
                lastDockTypes.forEach(dockType => !dockTypes.includes(dockType) && document.querySelector(dockType)?.click());
            }
        } else if(show === false) {
            // 隐藏侧边栏
            if (activeDocks.length > 0) {
                lastDockTypes = Array.from(activeDocks).map(dock => `[data-type="${dock.dataset.type}"]`);
                lastDockTypes.forEach(dockType => document.querySelector(dockType)?.click());
            } else {
                lastDockTypes = [];
            }
        } else {
            // 自动触发
            if (activeDocks.length > 0) {
                lastDockTypes = Array.from(activeDocks).map(dock => `[data-type="${dock.dataset.type}"]`);
                lastDockTypes.forEach(dockType => document.querySelector(dockType)?.click());
            } else if (lastDockTypes.length > 0) {
                lastDockTypes.forEach(dockType => document.querySelector(dockType)?.click());
            }
        }
        lastProtyle?.querySelector('.protyle-wysiwyg')?.focus();
    }
})();
image.png

留下你的脚步
推荐阅读