Multi-Column Layout for printing (Print Only)

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Multi-Column Layout for printing (Print Only)
// @namespace    http://tampermonkey.net/
// @license MIT
// @version      2.5
// @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';

    // Add colorful title and usage instructions right after initialization
    console.log('%c Multi-Column Layout for Printing v2.3 ',
        'background: #4A90E2; color: white; font-size: 14px; font-weight: bold; padding: 8px; border-radius: 4px; text-shadow: 1px 1px 1px rgba(0,0,0,0.2);');
    console.log('%c 📖 Usage: Press Ctrl+S to open settings, customize your layout, then Ctrl+P to print! ',
        'background: #2ECC71; color: white; font-size: 12px; padding: 6px; border-radius: 4px;');


    // Default configuration
    const DEFAULT_CONFIG = {
        columns: 1,
        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');
        return savedConfig ? {...DEFAULT_CONFIG, ...JSON.parse(savedConfig)} : DEFAULT_CONFIG;
    }

    function saveConfig(config) {
        localStorage.setItem('printLayoutConfig', JSON.stringify(config));
        CONFIG = config;
        updateStyles();
    }

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

    // Create and inject the configuration UI
    function createConfigUI() {

        // 如果已经存在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);

        // Save button handler
        const saveButton = configModal.querySelector('.save-button');
        if (saveButton) {
            saveButton.addEventListener('click', () => {
                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', () => {
                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() {
        if (configModal) {
            configModal.style.setProperty('display', 'none', 'important');
        }
    }

    function toggleConfigModal() {
        if (!configModal || configModal.style.display === 'none') {
            showConfigModal();
        } else {
            hideConfigModal();
        }
    }

    // Create and update styles based on current config
    function updateStyles() {
        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() {
        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() {
        applyPrintColumns();
    }

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

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


})();