Multi-Column Layout for printing (Print Only)

Convert single column layout to multi-column layout only when printing. Press Ctrl+S to open settings.

当前为 2024-11-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Multi-Column Layout for printing (Print Only)
// @namespace    http://tampermonkey.net/
// @license MIT
// @version      2.3
// @description  Convert single column layout to multi-column layout only when printing. Press Ctrl+S to open settings.
// @author       KQ yang
// @match        *://*
// @match        file:///*
// @match        http://127.0.0.1:*/*
// @match        http://localhost:*/*
// @grant        GM_addStyle
// @run-at       document-start
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    console.log('Script initialized'); // 调试日志

    // Default configuration
    const DEFAULT_CONFIG = {
        columns: 2,
        columnGap: '30px',
        fontSize: '16px',
        paragraphSpacing: '1em',
        enablePageBreak: true,
        lineHeight: '1.5',
    };

    // Load config from localStorage or use defaults
    let CONFIG = loadConfig();

    function loadConfig() {
        const savedConfig = localStorage.getItem('printLayoutConfig');
        console.log('Loaded config:', savedConfig); // 调试日志
        return savedConfig ? {...DEFAULT_CONFIG, ...JSON.parse(savedConfig)} : DEFAULT_CONFIG;
    }

    function saveConfig(config) {
        localStorage.setItem('printLayoutConfig', JSON.stringify(config));
        CONFIG = config;
        console.log('Saved config:', config); // 调试日志
        updateStyles();
    }

    let configModal = null; // 将configModal提升为全局变量

    // Create and inject the configuration UI
    function createConfigUI() {
        console.log('Creating config UI'); // 调试日志

        // 如果已经存在modal,先移除
        if (configModal) {
            configModal.remove();
        }

        configModal = document.createElement('div');
        configModal.id = 'print-layout-config-modal';
        configModal.setAttribute('style', `
            all: initial;
            position: fixed !important;
            top: 50% !important;
            left: 50% !important;
            transform: translate(-50%, -50%) !important;
            background: white !important;
            padding: 30px !important;
            border-radius: 12px !important;
            box-shadow: 0 8px 24px rgba(0,0,0,0.15) !important;
            z-index: 2147483647 !important;
            display: none !important;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif !important;
            min-width: 320px !important;
            max-width: 90vw !important;
            animation: modalFadeIn 0.3s ease-out !important;
            color: black !important;
        `);

        const styleElement = document.createElement('style');
        styleElement.textContent = `
            @keyframes modalFadeIn {
                from {
                    opacity: 0;
                    transform: translate(-50%, -48%);
                }
                to {
                    opacity: 1;
                    transform: translate(-50%, -50%);
                }
            }
            .settings-row {
                margin-bottom: 20px;
                display: flex;
                align-items: center;
                justify-content: space-between;
            }
            .settings-row label {
                color: #333;
                font-size: 14px;
                margin-right: 15px;
            }
            .settings-row input[type="number"],
            .settings-row input[type="text"] {
                width: 100px;
                padding: 8px;
                border: 1px solid #ddd;
                border-radius: 6px;
                font-size: 14px;
                transition: border-color 0.2s;
            }
            .settings-row input:focus {
                outline: none;
                border-color: #4A90E2;
                box-shadow: 0 0 0 2px rgba(74,144,226,0.2);
            }
            .settings-row input[type="checkbox"] {
                width: 18px;
                height: 18px;
                cursor: pointer;
            }
            .modal-title {
                color: #333;
                margin: 0 0 25px 0;
                font-size: 18px;
                font-weight: 600;
                border-bottom: 2px solid #eee;
                padding-bottom: 15px;
            }
            .modal-footer {
                margin-top: 25px;
                padding-top: 20px;
                border-top: 2px solid #eee;
                text-align: right;
            }
            .save-button {
                background: #4A90E2;
                color: white;
                border: none;
                padding: 10px 20px;
                border-radius: 6px;
                font-size: 14px;
                cursor: pointer;
                transition: background-color 0.2s;
            }
            .save-button:hover {
                background: #357ABD;
            }
            .close-button {
                position: absolute;
                top: 15px;
                right: 15px;
                background: none;
                border: none;
                font-size: 20px;
                cursor: pointer;
                color: #666;
                padding: 5px;
                line-height: 1;
            }
            .close-button:hover {
                color: #333;
            }
            #print-layout-config-modal {
                visibility: visible !important;
                display: block !important;
            }
        `;
        document.head.appendChild(styleElement);

        configModal.innerHTML = ''; // 清空现有内容
        const modalContent = document.createElement('div');
        modalContent.style.cssText = `
            all: initial;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
            color: black !important;
        `;
        modalContent.innerHTML = `
            <h3 class="modal-title">Print Layout Settings</h3>
            <button class="close-button" title="Close">×</button>
            <div class="settings-row">
                <label>Columns (1-4):</label>
                <input type="number" id="columns" min="1" max="4" value="${CONFIG.columns}">
            </div>
            <div class="settings-row">
                <label>Column Gap:</label>
                <input type="text" id="columnGap" value="${CONFIG.columnGap}">
            </div>
            <div class="settings-row">
                <label>Font Size:</label>
                <input type="text" id="fontSize" value="${CONFIG.fontSize}">
            </div>
            <div class="settings-row">
                <label>Paragraph Spacing:</label>
                <input type="text" id="paragraphSpacing" value="${CONFIG.paragraphSpacing}">
            </div>
            <div class="settings-row">
                <label>Line Height:</label>
                <input type="text" id="lineHeight" value="${CONFIG.lineHeight}">
            </div>
            <div class="settings-row">
                <label>Enable Page Break:</label>
                <input type="checkbox" id="enablePageBreak" ${CONFIG.enablePageBreak ? 'checked' : ''}>
            </div>
            <div class="modal-footer">
                <button class="save-button">Save Changes</button>
            </div>
        `;

        configModal.appendChild(modalContent);
        document.documentElement.appendChild(configModal);
        console.log('Config UI created and appended to body'); // 调试日志

        // Save button handler
        const saveButton = configModal.querySelector('.save-button');
        if (saveButton) {
            saveButton.addEventListener('click', () => {
                console.log('Save button clicked'); // 调试日志
                const newConfig = {
                    columns: parseInt(configModal.querySelector('#columns').value, 10),
                    columnGap: configModal.querySelector('#columnGap').value,
                    fontSize: configModal.querySelector('#fontSize').value,
                    paragraphSpacing: configModal.querySelector('#paragraphSpacing').value,
                    lineHeight: configModal.querySelector('#lineHeight').value,
                    enablePageBreak: configModal.querySelector('#enablePageBreak').checked
                };
                saveConfig(newConfig);
                configModal.style.setProperty('display', 'none', 'important');
            });
        }

        // Close button handler
        const closeButton = configModal.querySelector('.close-button');
        if (closeButton) {
            closeButton.addEventListener('click', () => {
                console.log('Close button clicked'); // 调试日志
                configModal.style.setProperty('display', 'none', 'important');
            });
        }

        // Click outside to close
        configModal.addEventListener('click', (e) => {
            if (e.target === configModal) {
                configModal.style.setProperty('display', 'none', 'important');
            }
        });

        return configModal;
    }

    function showConfigModal() {
        console.log('Showing config modal'); // 调试日志
        if (!configModal) {
            configModal = createConfigUI();
        }
        configModal.style.setProperty('display', 'block', 'important');
        configModal.style.setProperty('visibility', 'visible', 'important');
    }

    function hideConfigModal() {
        console.log('Hiding config modal'); // 调试日志
        if (configModal) {
            configModal.style.setProperty('display', 'none', 'important');
        }
    }

    function toggleConfigModal() {
        console.log('Toggling config modal'); // 调试日志
        if (!configModal || configModal.style.display === 'none') {
            showConfigModal();
        } else {
            hideConfigModal();
        }
    }

    // Create and update styles based on current config
    function updateStyles() {
        console.log('Updating styles'); // 调试日志
        const styleSheet = `
            @media print {
                html, body {
                    margin: 0 !important;
                    padding: 0 !important;
                    min-height: 0 !important;
                    height: auto !important;
                }
                .print-column-container {
                    column-count: ${CONFIG.columns} !important;
                    column-gap: ${CONFIG.columnGap} !important;
                    column-rule: 1px solid #ddd !important;
                    width: 100% !important;
                    margin: 0 !important;
                    padding: 0 !important;
                    min-height: 0 !important;
                    height: auto !important;
                    overflow: visible !important;
                    box-sizing: border-box !important;
                    font-size: ${CONFIG.fontSize} !important;
                    line-height: ${CONFIG.lineHeight} !important;
                    ${CONFIG.enablePageBreak ? '' : 'page-break-inside: avoid !important;'}
                }
                .print-column-container > * {
                    break-inside: avoid !important;
                    margin-bottom: ${CONFIG.paragraphSpacing} !important;
                    max-width: 100% !important;
                    box-sizing: border-box !important;
                    page-break-inside: avoid !important;
                }
                .print-column-container img {
                    max-width: 100% !important;
                    height: auto !important;
                    page-break-inside: avoid !important;
                }
            }
        `;

        const existingStyle = document.getElementById('print-layout-style');
        if (existingStyle) {
            existingStyle.remove();
        }

        const style = document.createElement('style');
        style.id = 'print-layout-style';
        style.textContent = styleSheet;
        document.head.appendChild(style);
    }

    // Apply columns to main content
    function applyPrintColumns() {
        console.log('Applying print columns'); // 调试日志
        const mainContent = document.querySelector('.target-content') || document.body;
        mainContent.classList.add('print-column-container');

        const printStyle = document.createElement('style');
        printStyle.media = 'print';
        printStyle.textContent = `
            @page {
                margin: 1cm !important;
                padding: 0 !important;
                size: auto !important;
            }
        `;
        document.head.appendChild(printStyle);
    }

    // Initialize modal globally
    configModal = createConfigUI();

    // Handle Ctrl+S shortcut with improved event handling
    document.addEventListener('keydown', function(e) {
        if (e.ctrlKey && (e.key === 's' || e.key === 'S' || e.keyCode === 83)) {
            console.log('Ctrl+S detected'); // 调试日志
            e.stopPropagation();
            e.preventDefault();
            toggleConfigModal();
            return false;
        }
    }, true);

    // Initial style application
    updateStyles();

    // Handle DOMContentLoaded
    function onDOMContentLoaded() {
        console.log('DOM Content Loaded'); // 调试日志
        applyPrintColumns();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', onDOMContentLoaded);
    } else {
        onDOMContentLoaded();
    }

    // Add a global function for testing
    window.togglePrintLayoutConfig = toggleConfigModal;

    // Log usage instructions to console
    console.log(`
    Multi-Column Layout Userscript Usage:
    -----------------------------------
    1. Press Ctrl+S (or Cmd+S on Mac) to open settings
    2. Adjust your preferences:
       - Columns: Number of columns (1-4)
       - Column Gap: Space between columns
       - Font Size: Text size in print
       - Paragraph Spacing: Space between paragraphs
       - Line Height: Text line height
       - Enable Page Break: Allow/prevent content breaks across pages
    3. Click Save Changes to apply settings
    4. Settings are automatically saved between sessions
    5. Press Ctrl+P to see the print preview with your settings

    For testing, you can also use: window.togglePrintLayoutConfig()

    Current configuration:`, CONFIG);

})();