Local TXT Reader

Enhanced reading experience for local txt files with Element UI style and improved themes

当前为 2024-09-17 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Local TXT Reader
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Enhanced reading experience for local txt files with Element UI style and improved themes
// @author       JiuYou2020
// @license      MIT
// @match        file:///*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // Ensure the script runs only on .txt files
    if (window.location.pathname.endsWith('.txt')) {
        // Set up styles
        const style = document.createElement('style');
        style.innerHTML = `
            body {
                margin: 0;
                padding: 0;
                display: flex;
                height: 100vh;
                overflow: hidden;
                transition: background-color 0.3s, color 0.3s;
                background-size: cover;
                background-position: center;
            }
            .outer-container {
                flex: 1;
                overflow-y: auto;
                scroll-behavior: smooth;
                padding: 20px;
            }
            .content-container {
                width: 55%;
                margin: 0 auto;
                transition: font-size 0.3s, line-height 0.3s, background-color 0.3s, color 0.3s;
                padding: 20px;
                border-radius: 10px;
            }
            .content-container p {
                text-indent: 2em;
                margin-top: 1em;
            }
            #settingsBtn {
                position: fixed;
                right: 20px;
                bottom: 20px;
                z-index: 1000;
                padding: 12px;
                background-color: #409EFF;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 16px;
                box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
                transition: background-color 0.3s;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            #settingsBtn:hover {
                background-color: #66b1ff;
            }
            #settingsPanel {
                display: none;
                position: fixed;
                right: 20px;
                bottom: 80px;
                background-color: white;
                border: 1px solid #EBEEF5;
                border-radius: 4px;
                padding: 20px;
                z-index: 1000;
                box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
            }
            #settingsPanel div {
                margin-bottom: 15px;
            }
            #settingsPanel button {
                margin-right: 5px;
                padding: 8px 15px;
                border: 1px solid #DCDFE6;
                border-radius: 4px;
                cursor: pointer;
                transition: all 0.3s;
                background-color: white;
                color: #606266;
            }
            #settingsPanel button:hover {
                color: #409EFF;
                border-color: #c6e2ff;
                background-color: #ecf5ff;
            }
            .theme-btn {
                width: 30px;
                height: 30px;
                border-radius: 4px;
            }
            #classicTheme { background-color: #e7e3d8; }
            #whiteTheme { background-color: #ffffff; border: 1px solid #DCDFE6 !important; }
            #darkTheme { background-color: #1e1e1e; }
            .font-btn, .line-height-btn {
                background-color: #409EFF !important;
                color: white !important;
                border: none !important;
            }
            .font-btn:hover, .line-height-btn:hover {
                background-color: #66b1ff !important;
                color: white !important;
            }
            #resetDefault {
                background-color: #F56C6C !important;
                color: white !important;
                border: none !important;
            }
            #resetDefault:hover {
                background-color: #f78989 !important;
            }
            #customBgInput {
                display: none;
            }
            #customBgLabel {
                display: inline-block;
                padding: 8px 15px;
                background-color: #67C23A;
                color: white;
                border-radius: 4px;
                cursor: pointer;
                transition: background-color 0.3s;
            }
            #customBgLabel:hover {
                background-color: #85ce61;
            }
        `;
        document.head.appendChild(style);

        // Get the text content
        const text = document.body.textContent.trim();

        // Split the text into paragraphs by newlines
        const paragraphs = text.split(/\n+/).map(paragraph => paragraph.trim());

        // Clear the body content
        document.body.innerHTML = '';

        // Create the outer container
        const outerContainer = document.createElement('div');
        outerContainer.className = 'outer-container';
        document.body.appendChild(outerContainer);

        // Create the content container
        const contentContainer = document.createElement('div');
        contentContainer.className = 'content-container';
        outerContainer.appendChild(contentContainer);

        // Add paragraphs to the content container
        paragraphs.forEach(paragraph => {
            if (paragraph) { // Only add non-empty paragraphs
                const p = document.createElement('p');
                p.textContent = paragraph;
                contentContainer.appendChild(p);
            }
        });

        // Create settings button
        const settingsBtn = document.createElement('button');
        settingsBtn.id = 'settingsBtn';
        settingsBtn.textContent = '设置';
        document.body.appendChild(settingsBtn);

        // Create settings panel
        const settingsPanel = document.createElement('div');
        settingsPanel.id = 'settingsPanel';
        settingsPanel.innerHTML = `
            <div>
                大小:
                <button id="decreaseFont" class="font-btn">-</button>
                <span id="currentFontSize"></span>
                <button id="increaseFont" class="font-btn">+</button>
            </div>
            <div>
                行高:
                <button id="decreaseLineHeight" class="line-height-btn">-</button>
                <span id="currentLineHeight"></span>
                <button id="increaseLineHeight" class="line-height-btn">+</button>
            </div>
            <div>
                主题:
                <button id="classicTheme" class="theme-btn" title="经典"></button>
                <button id="whiteTheme" class="theme-btn" title="纯白"></button>
                <button id="darkTheme" class="theme-btn" title="暗黑"></button>
            </div>
            <div>
                <label for="customBgInput" id="customBgLabel">自定义背景</label>
                <input type="file" id="customBgInput" accept="image/*">
            </div>
            <div>
                <button id="resetDefault">恢复默认设置</button>
            </div>
        `;
        document.body.appendChild(settingsPanel);

        // Define themes
        const themes = {
            classic: { bg: '#e7e3d8', text: '#333333', contentBg: '#f1ede4' },
            white: { bg: '#ffffff', text: '#333333', contentBg: '#ffffff' },
            dark: { bg: '#1e1e1e', text: '#ffffff', contentBg: 'rgba(30, 30, 30, 0.8)' }
        };

        // Function to save settings
        function saveSettings() {
            const settings = {
                fontSize: contentContainer.style.fontSize,
                lineHeight: contentContainer.style.lineHeight,
                theme: currentTheme,
                customBg: document.body.style.backgroundImage,
                btnPosition: {
                    right: settingsBtn.style.right,
                    bottom: settingsBtn.style.bottom
                }
            };
            GM_setValue('readerSettings', JSON.stringify(settings));
        }

        // Function to load settings
        function loadSettings() {
            const savedSettings = GM_getValue('readerSettings', null);
            if (savedSettings) {
                const settings = JSON.parse(savedSettings);
                contentContainer.style.fontSize = settings.fontSize || '19px';
                contentContainer.style.lineHeight = settings.lineHeight || '1.5';
                applyTheme(settings.theme || 'classic');
                if (settings.customBg) {
                    document.body.style.backgroundImage = settings.customBg;
                }
                if (settings.btnPosition) {
                    settingsBtn.style.right = settings.btnPosition.right;
                    settingsBtn.style.bottom = settings.btnPosition.bottom;
                }
            } else {
                resetToDefault();
            }
        }

        // Function to apply theme
        function applyTheme(theme) {
            if (theme !== 'custom') {
                document.body.style.backgroundColor = themes[theme].bg;
                document.body.style.color = themes[theme].text;
                contentContainer.style.backgroundColor = themes[theme].contentBg;
                document.body.style.backgroundImage = '';
            }
            currentTheme = theme;
        }

        // Function to reset to default settings
        function resetToDefault() {
            contentContainer.style.fontSize = '19px';
            contentContainer.style.lineHeight = '1.5';
            applyTheme('classic');
            document.body.style.backgroundImage = '';
            settingsBtn.style.right = '20px';
            settingsBtn.style.bottom = '20px';
            updateSettingsDisplay();
            saveSettings();
        }

        // Load settings
        let currentTheme = 'classic';
        loadSettings();

        // Function to save the current scroll position
        function saveScrollPosition() {
            const scrollPosition = outerContainer.scrollTop;
            const fileName = window.location.pathname.split('/').pop();
            GM_setValue(fileName, scrollPosition);
        }

        // Function to load the saved scroll position
        function loadScrollPosition() {
            const fileName = window.location.pathname.split('/').pop();
            const savedPosition = GM_getValue(fileName, 0);
            outerContainer.scrollTop = savedPosition;
        }

        // Load the saved position when the page loads
        loadScrollPosition();

        // Save the position when the user scrolls
        outerContainer.addEventListener('scroll', saveScrollPosition);

        // Handle arrow key navigation
        document.addEventListener('keydown', function(e) {
            const scrollAmount = 100; // Adjust this value to change scroll speed
            if (e.key === 'ArrowUp') {
                outerContainer.scrollBy({top: -scrollAmount, behavior: 'smooth'});
                e.preventDefault();
            } else if (e.key === 'ArrowDown') {
                outerContainer.scrollBy({top: scrollAmount, behavior: 'smooth'});
                e.preventDefault();
            }
        });

        // Toggle settings panel
        settingsBtn.addEventListener('click', () => {
            settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none';
            updatePanelPosition();
        });

        // Make settings button draggable
        let isDragging = false;
        let dragOffsetX, dragOffsetY;

        settingsBtn.addEventListener('mousedown', (e) => {
            isDragging = true;
            dragOffsetX = e.clientX - settingsBtn.offsetLeft;
            dragOffsetY = e.clientY - settingsBtn.offsetTop;
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const newX = e.clientX - dragOffsetX;
                const newY = e.clientY - dragOffsetY;
                settingsBtn.style.right = `${document.body.clientWidth - newX - settingsBtn.offsetWidth}px`;
                settingsBtn.style.bottom = `${document.body.clientHeight - newY - settingsBtn.offsetHeight}px`;
                updatePanelPosition();
            }
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                saveSettings();
            }
        });

        // Update settings panel position
        function updatePanelPosition() {
            const btnRect = settingsBtn.getBoundingClientRect();
            settingsPanel.style.right = settingsBtn.style.right;
            settingsPanel.style.bottom = `${parseFloat(settingsBtn.style.bottom) + btnRect.height + 10}px`;
        }

        // Font size controls
        document.getElementById('decreaseFont').addEventListener('click', () => {
            const currentSize = parseInt(contentContainer.style.fontSize);
            if (currentSize > 10) {
                contentContainer.style.fontSize = (currentSize - 1) + 'px';
                updateSettingsDisplay();
                saveSettings();
            }
        });

        document.getElementById('increaseFont').addEventListener('click', () => {
            const currentSize = parseInt(contentContainer.style.fontSize);
            if (currentSize < 30) {
                contentContainer.style.fontSize = (currentSize + 1) + 'px';
                updateSettingsDisplay();
                saveSettings();
            }
        });

        // Line height controls
        document.getElementById('decreaseLineHeight').addEventListener('click', () => {
            const currentHeight = parseFloat(contentContainer.style.lineHeight);
            if (currentHeight > 1) {
                contentContainer.style.lineHeight = (currentHeight - 0.1).toFixed(1);
                updateSettingsDisplay();
                saveSettings();
            }
        });

        document.getElementById('increaseLineHeight').addEventListener('click', () => {
            const currentHeight = parseFloat(contentContainer.style.lineHeight);
            if (currentHeight < 3) {
                contentContainer.style.lineHeight = (currentHeight + 0.1).toFixed(1);
                updateSettingsDisplay();
                saveSettings();
            }
        });

        // Theme controls
        document.getElementById('classicTheme').addEventListener('click', () => {
            applyTheme('classic');
            saveSettings();
        });

        document.getElementById('whiteTheme').addEventListener('click', () => {
            applyTheme('white');
            saveSettings();
        });

        document.getElementById('darkTheme').addEventListener('click', () => {
            applyTheme('dark');
            saveSettings();
        });

        // Custom background
        document.getElementById('customBgInput').addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    document.body.style.backgroundImage = `url(${e.target.result})`;
                    currentTheme = 'custom';
                    saveSettings();
                }
                reader.readAsDataURL(file);
            }
        });

        // Reset to default
        document.getElementById('resetDefault').addEventListener('click', resetToDefault);

        // Update display of current settings
        function updateSettingsDisplay() {
            document.getElementById('currentFontSize').textContent = contentContainer.style.fontSize;
            document.getElementById('currentLineHeight').textContent = contentContainer.style.lineHeight;
        }

        updateSettingsDisplay();
    }
})();