高效文本文件查看器

优化文本文件查看体验,支持快速复制、下载及长文本处理

// ==UserScript==
// @name         高效文本文件查看器
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  优化文本文件查看体验,支持快速复制、下载及长文本处理
// @author       niweizhuan
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function() {
	'use strict';

	// 获取当前页面的URL
	const currentPageUrl = window.location.href;

	// 检查是否为txt文件
	function isTxtFile() {
		// 获取当前页面的Content-Type
		var contentType = document.contentType;

		// 检查Content-Type是否为纯文本
		return contentType === 'text/plain';
	}
	// 异步加载文本内容(流式读取)
	function loadTextContent(url) {
		return fetch(url).then(function(response) {
			if (!response.ok) {
				throw new Error('请检查互联网连接');
			}
			const reader = response.body.getReader();
			const decoder = new TextDecoder('utf-8');
			let content = [];
			return new Promise(function(resolve, reject) {
				function read() {
					reader.read().then(function({
						done,
						value
					}) {
						if (done) {
							resolve(content.join(''));
							return;
						}
						content.push(decoder.decode(value, {
							stream: true
						}));
						read();
					}).catch(reject);
				}
				read();
			});
		}).catch(function(error) {
			alert('加载失败:', error);
			return null;
		});
	}

	// 分段显示内容
	function displayContent(content) {
		const contentElement = document.getElementById('content');
		const loadingElement = document.getElementById('loading');
		const errorElement = document.getElementById('error');

		loadingElement.style.display = 'block';
		errorElement.style.display = 'none';

		if (!content) {
			errorElement.style.display = 'block';
			loadingElement.style.display = 'none';
			return;
		}

		const chunkSize = 10000;
		let offset = 0;
		window.textContentInterval = setInterval(function() {
			const chunk = content.slice(offset, offset + chunkSize);
			const chunkElement = document.createElement('div');
			chunkElement.textContent = chunk;
			contentElement.appendChild(chunkElement);
			offset += chunkSize;
			if (offset >= content.length) {
				clearInterval(window.textContentInterval);
				loadingElement.style.display = 'none';
			}
		}, 100);
	}

	// 初始化页面
	function initializePage() {
		const newPageContent = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>文本文件查看器</title>
            <style>
                body {
                    font-family: Arial, sans-serif;
                    padding: 20px;
                    max-width: 800px;
                    margin: 0 auto;
                    background-color: #f0f0f0;
                    overflow: hidden;
                }
                #header {
                    margin-bottom: 20px;
                }
                #header button {
                    margin-right: 10px;
                    padding: 8px 16px;
                    font-size: 14px;
                    background-color: #007bff;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                }
                #header button:hover {
                    background-color: #0056b3;
                }
                #content {
                    white-space: pre-wrap;
                    background-color: #f9f9f9;
                    border: 1px solid #ddd;
                    padding: 10px;
                    border-radius: 4px;
                    margin-top: 20px;
                    font-family: monospace;
                    font-size: 14px;
                    max-height: 600px;
                    overflow-y: auto;
                    will-change: transform;
                }
                #error {
                    color: red;
                    font-weight: bold;
                    margin-top: 20px;
                    display: none;
                }
                #loading {
                    text-align: center;
                    margin-top: 50px;
                    display: block;
                }
                #loading img {
                    width: 50px;
                    height: 50px;
                }
                @keyframes rotate {
                    0% {
                        transform: rotate(0deg);
                    }
                    100% {
                        transform: rotate(360deg);
                    }
                }

                .loader {
                    width: 50px;
                    height: 50px;
                    border: 5px solid transparent;
                    border-top-color: #007bff;
                    border-bottom-color: #007bff;
                    border-radius: 50%;
                    animation: rotate 1s linear infinite;
                    margin: 100px auto;
                }
            </style>
        </head>
        <body>
            <div id="header">
                <button id="copyAll">复制全部</button>
                <button id="downloadText">下载文字</button>
            </div>
            <div id="content" class="scroll-container"></div>
            <div id="custom-txt-viewer"></div>
            <div id="error">加载失败,请检查链接是否正确。</div>
            <div id="loading">
                <div class="loader"></div>
                加载中...
            </div>
        </body>
        </html>
        `;

		document.open();
		document.write(newPageContent);
		document.close();

		// 初始化按钮事件
		document.getElementById('copyAll').addEventListener('click', () => {
			const contentElement = document.getElementById('content');
			const content = contentElement.textContent;

			// 如果内容过长,禁止复制并提示用户下载
			if (content.length > 100000) {
				alert('内容过长,无法复制,请使用下载功能保存内容。');
				return;
			}

			const copyButton = document.getElementById('copyAll');
			copyButton.disabled = true;

			if (navigator.clipboard && window.isSecureContext) {
				// 使用Clipboard API复制
				navigator.clipboard.writeText(content)
					.then(() => {
						alert('内容已复制到剪贴板!');
					})
					.catch(err => {
						console.error('复制失败:', err);
						alert('复制失败,请手动复制内容或使用下载功能保存内容。', '详细信息:', err);
					})
					.finally(() => {
						copyButton.disabled = false;
					});
			} else {
				// 使用document.execCommand作为回退方案
				const textArea = document.createElement('textarea');
				textArea.value = content;
				document.body.appendChild(textArea);
				textArea.select();
				try {
					const successful = document.execCommand('copy');
					if (successful) {
						alert('内容已复制到剪贴板!');
					} else {
						alert('复制失败,请手动复制内容或使用下载功能保存内容。');
					}
				} catch (err) {
					console.error('无法执行复制操作:', err);
					alert('复制失败,请手动复制内容或使用下载功能保存内容。', '详细信息:', err);
				} finally {
					document.body.removeChild(textArea);
					copyButton.disabled = false;
				}
			}
		});
		document.getElementById('downloadText').addEventListener('click', function() {
			const contentElement = document.getElementById('content');
			const blob = new Blob([contentElement.textContent], {
				type: 'text/plain'
			});
			const url = URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = url;
			a.download = 'content.txt';
			a.click();
			URL.revokeObjectURL(url);
		});
	}

	// 检查是否为文件并初始化页面
	if (isTxtFile()) {
		initializePage();

		// 加载并显示内容
		(function() {
			loadTextContent(currentPageUrl).then(function(content) {
				if (content) {
					displayContent(content);
				}
			}).catch(function(error) {
				console.error('Error loading content:', error);
			});
		})();
	}
})();