网页文本收集器

增强版文本收储器:支持编辑、添加、导出等功能

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         网页文本收集器
// @namespace    https://violentmonkey.github.io/
// @version      0.3
// @description  增强版文本收储器:支持编辑、添加、导出等功能
// @author       小烧猪
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 使用 GM 存储实现跨域存储
    const storage = {
        save: function(key, value) {
            try {
                GM_setValue(key, value);
                return true;
            } catch (e) {
                console.error('存储失败:', e);
                return false;
            }
        },
        
        get: function(key, defaultValue = []) {
            try {
                const value = GM_getValue(key, defaultValue);
                return value || defaultValue;
            } catch (e) {
                console.error('读取失败:', e);
                return defaultValue;
            }
        }
    };

    // 添加更新笔记文本函数
    function updateNoteText(noteId, newText) {
        let notes = storage.get('notes', []);
        notes = notes.map(note => {
            if (note.id === noteId) {
                return { ...note, text: newText.trim() };
            }
            return note;
        });
        storage.save('notes', notes);
        updateNoteList();
        showToast('已更新笔记');
    }

    // 修改保存笔记函数
    function saveNote(text) {
        if (!text || text.trim() === '') {
            showToast('请输入内容!');
            return;
        }
        
        try {
            let notes = storage.get('notes', []);
            const newNote = {
                id: Date.now(),
                text: text.trim(),
                time: new Date().toLocaleString(),
                url: window.location.href,
                selected: false
            };
            
            notes.unshift(newNote);
            storage.save('notes', notes);
            
            const panel = document.getElementById('note-panel');
            if (panel) {
                panel.style.display = 'block';
                updateNoteList();
                showToast('已保存笔记!');
            }
        } catch (error) {
            console.error('保存笔记时出错:', error);
            showToast('保存失败,请重试!');
        }
    }

    // 修改删除笔记函数
    function deleteNote(noteId) {
        let notes = storage.get('notes', []);
        notes = notes.filter(note => note.id !== noteId);
        storage.save('notes', notes);
        updateNoteList();
        showToast('已删除笔记');
    }

    // 修改删除选中笔记函数
    function deleteSelectedNotes() {
        let notes = storage.get('notes', []);
        const selectedCount = notes.filter(note => note.selected).length;
        
        if (selectedCount === 0) {
            showToast('请先选择要删除的笔记');
            return;
        }
        
        notes = notes.filter(note => !note.selected);
        storage.save('notes', notes);
        updateNoteList();
        showToast(`已删除 ${selectedCount} 条笔记`);
    }

    // 修改切换选中状态函数
    function toggleNoteSelection(noteId) {
        let notes = storage.get('notes', []);
        notes = notes.map(note => {
            if (note.id === noteId) {
                return { ...note, selected: !note.selected };
            }
            return note;
        });
        storage.save('notes', notes);
        updateNoteList();
    }

    // 修改全选/取消全选函数
    function toggleSelectAll() {
        let notes = storage.get('notes', []);
        const allSelected = notes.length > 0 && notes.every(note => note.selected);
        
        notes = notes.map(note => ({
            ...note,
            selected: !allSelected
        }));
        
        storage.save('notes', notes);
        updateNoteList();
        showToast(allSelected ? '已取消全选' : '已全选');
    }

    // 修改搜索笔记函数
    function searchNotes(keyword) {
        const notes = storage.get('notes', []);
        const filtered = notes.filter(note => 
            note.text.toLowerCase().includes(keyword.toLowerCase())
        );
        renderNotes(filtered);
    }

    // 修改更新笔记列表函数
    function updateNoteList() {
        const noteList = document.getElementById('note-list');
        if (!noteList) return;
        
        const notes = storage.get('notes', []);
        renderNotes(notes);
    }

    // 创建浮动按钮
    function createFloatButton() {
        const btn = document.createElement('div');
        btn.innerHTML = '📝';
        btn.style.cssText = `
            position: fixed;
            right: 20px;
            bottom: 20px;
            width: 50px;
            height: 50px;
            background: #4CAF50;
            color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 24px;
            z-index: 10000;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        `;
        document.body.appendChild(btn);
        return btn;
    }

    // 创建按钮的辅助函数
    function createButton(text) {
        const btn = document.createElement('button');
        btn.innerHTML = text;
        btn.style.cssText = `
            padding: 6px 12px;
            border: none;
            border-radius: 6px;
            background: #4CAF50;
            color: white;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s;
        `;
        return btn;
    }

    // 创建小按钮的辅助函数
    function createSmallButton(text) {
        const btn = document.createElement('button');
        btn.innerHTML = text;
        btn.style.cssText = `
            padding: 4px 8px;
            border: none;
            border-radius: 4px;
            background: #4CAF50;
            color: white;
            cursor: pointer;
            font-size: 12px;
            transition: all 0.2s;
        `;
        return btn;
    }

    // 创建面板
    function createPanel() {
        const panel = document.createElement('div');
        panel.id = 'note-panel';
        panel.style.cssText = `
            position: fixed;
            right: 80px;
            bottom: 20px;
            width: 350px;
            max-height: 500px;
            background: white;
            border-radius: 12px;
            padding: 16px;
            display: none;
            z-index: 9999;
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
            overflow-y: auto;
        `;

        // 工具栏
        const toolbar = document.createElement('div');
        toolbar.style.cssText = `
            display: flex;
            gap: 8px;
            margin-bottom: 12px;
            flex-wrap: wrap;
        `;

        // 添加按钮
        const addBtn = createButton('📝 新建');
        const exportBtn = createButton('📤 导出');
        const selectAllBtn = createButton('☑️ 全选');
        const deleteSelectedBtn = createButton('🗑️ 删除');
        
        addBtn.onclick = addNewNote;
        exportBtn.onclick = exportToMd;
        selectAllBtn.onclick = toggleSelectAll;
        deleteSelectedBtn.onclick = deleteSelectedNotes;
        
        deleteSelectedBtn.style.background = '#dc3545';

        toolbar.appendChild(addBtn);
        toolbar.appendChild(exportBtn);
        toolbar.appendChild(selectAllBtn);
        toolbar.appendChild(deleteSelectedBtn);

        // 搜索框
        const searchBox = document.createElement('input');
        searchBox.style.cssText = `
            width: 100%;
            padding: 8px;
            margin: 8px 0;
            border: 1px solid #ddd;
            border-radius: 6px;
        `;
        searchBox.placeholder = '搜索笔记...';
        searchBox.oninput = (e) => searchNotes(e.target.value);

        // 笔记列表容器
        const noteList = document.createElement('div');
        noteList.id = 'note-list';

        panel.appendChild(toolbar);
        panel.appendChild(searchBox);
        panel.appendChild(noteList);
        document.body.appendChild(panel);

        return panel;
    }

    // 添加显示提示函数
    function showToast(message) {
        const toast = document.createElement('div');
        toast.style.cssText = `
            position: fixed;
            bottom: 80px;
            right: 20px;
            background: rgba(76, 175, 80, 0.9);
            color: white;
            padding: 8px 16px;
            border-radius: 4px;
            font-size: 14px;
            z-index: 10001;
            pointer-events: none;
            animation: fadeIn 0.3s ease;
        `;
        toast.textContent = message;
        document.body.appendChild(toast);
        
        setTimeout(() => {
            toast.style.animation = 'fadeOut 0.3s ease';
            setTimeout(() => {
                if (document.body.contains(toast)) {
                    document.body.removeChild(toast);
                }
            }, 300);
        }, 2000);
    }

    // 添加动画样式
    GM_addStyle(`
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        @keyframes fadeOut {
            from { opacity: 1; transform: translateY(0); }
            to { opacity: 0; transform: translateY(20px); }
        }
    `);

    // 修改新建笔记功能
    function addNewNote() {
        const dialog = document.createElement('div');
        dialog.id = 'note-dialog';
        dialog.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.2);
            z-index: 10002;
            width: 400px;
        `;
        
        const textarea = document.createElement('textarea');
        textarea.style.cssText = `
            width: 100%;
            height: 100px;
            padding: 8px;
            margin: 8px 0;
            border: 1px solid #ddd;
            border-radius: 6px;
            resize: vertical;
        `;
        
        const btnContainer = document.createElement('div');
        btnContainer.style.cssText = `
            display: flex;
            justify-content: flex-end;
            gap: 8px;
            margin-top: 16px;
        `;
        
        const saveBtn = createButton('保存');
        const cancelBtn = createButton('取消');
        cancelBtn.style.background = '#666';
        
        // 修改保存按钮点击事件
        saveBtn.onclick = () => {
            const text = textarea.value;
            if (text.trim()) {
                saveNote(text);
                const dialogElement = document.getElementById('note-dialog');
                if (dialogElement) {
                    document.body.removeChild(dialogElement);
                }
            } else {
                showToast('请输入内容!');
            }
        };
        
        // 修改取消按钮点击事件
        cancelBtn.onclick = () => {
            const dialogElement = document.getElementById('note-dialog');
            if (dialogElement) {
                document.body.removeChild(dialogElement);
            }
        };
        
        // 添加按键事件支持
        textarea.onkeydown = (e) => {
            if (e.key === 'Enter' && e.ctrlKey) {
                e.preventDefault();
                saveBtn.click();
            } else if (e.key === 'Escape') {
                e.preventDefault();
                cancelBtn.click();
            }
        };
        
        btnContainer.appendChild(cancelBtn);
        btnContainer.appendChild(saveBtn);
        
        dialog.appendChild(textarea);
        dialog.appendChild(btnContainer);
        document.body.appendChild(dialog);
        
        textarea.focus();
    }

    // 修改导出为Markdown功能
    function exportToMd() {
        const notes = storage.get('notes', []);
        const selectedNotes = notes.filter(note => note.selected);
        
        if (selectedNotes.length === 0) {
            showToast('请先选择要导出的笔记');
            return;
        }
        
        const markdown = selectedNotes.reverse().map(note => {
            return `## ${note.time}\n\n${note.text}\n\n[源链接](${note.url})\n\n---\n`;
        }).join('\n');
        
        const blob = new Blob([markdown], {type: 'text/markdown'});
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `笔记导出_${new Date().toLocaleDateString()}.md`;
        a.click();
        URL.revokeObjectURL(url);
        
        showToast('导出成功!');
    }

    // 修改初始化函数
    function initialize() {
        const floatBtn = createFloatButton();
        const panel = createPanel();
        
        floatBtn.onclick = function() {
            const isHidden = panel.style.display === 'none';
            panel.style.display = isHidden ? 'block' : 'none';
            if (isHidden) {
                updateNoteList();
            }
        };

        document.addEventListener('copy', handleCopy);
    }

    // 直接调用初始化
    initialize();

    // 修改渲染笔记函数,确保正确显示
    function renderNotes(notes) {
        const noteList = document.getElementById('note-list');
        if (!noteList) return;
        
        noteList.innerHTML = '';
        
        if (!notes || notes.length === 0) {
            noteList.innerHTML = '<div style="text-align: center; color: #666; padding: 20px;">暂无笔记</div>';
            return;
        }
        
        notes.forEach(note => {
            const noteDiv = document.createElement('div');
            noteDiv.style.cssText = `
                border: 1px solid #eee;
                border-radius: 8px;
                padding: 12px;
                margin: 8px 0;
                background: #fafafa;
            `;

            // 笔记内容区域
            const contentWrapper = document.createElement('div');
            contentWrapper.style.cssText = `
                display: flex;
                gap: 8px;
                align-items: start;
            `;

            // 复选框
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.checked = note.selected;
            checkbox.style.marginTop = '3px';
            checkbox.onchange = () => toggleNoteSelection(note.id);

            // 可编辑的内容区域
            const content = document.createElement('div');
            content.textContent = note.text;
            content.style.cssText = `
                flex-grow: 1;
                margin: 8px 0;
                padding: 8px;
                min-height: 20px;
                border-radius: 4px;
            `;
            content.onclick = function() {
                content.contentEditable = true;
                content.focus();
            };
            content.onblur = function() {
                content.contentEditable = false;
                if (content.textContent !== note.text) {
                    updateNoteText(note.id, content.textContent);
                }
            };

            contentWrapper.appendChild(checkbox);
            contentWrapper.appendChild(content);

            // 按钮组
            const buttonGroup = document.createElement('div');
            buttonGroup.style.cssText = `
                display: flex;
                gap: 4px;
                margin-top: 8px;
                justify-content: flex-end;
            `;

            // 复制按钮
            const copyBtn = createSmallButton('📋 复制');
            copyBtn.onclick = () => {
                navigator.clipboard.writeText(note.text);
                showToast('已复制到剪贴板');
            };

            // 保存按钮
            const saveBtn = createSmallButton('💾 保存');
            saveBtn.onclick = () => {
                updateNoteText(note.id, content.textContent);
                showToast('已保存更改');
            };

            // 删除按钮
            const deleteBtn = createSmallButton('🗑️ 删除');
            deleteBtn.style.background = '#dc3545';
            deleteBtn.onclick = () => deleteNote(note.id);

            buttonGroup.appendChild(copyBtn);
            buttonGroup.appendChild(saveBtn);
            buttonGroup.appendChild(deleteBtn);

            noteDiv.appendChild(contentWrapper);
            noteDiv.appendChild(buttonGroup);
            noteList.appendChild(noteDiv);
        });
    }

    // 修改复制事件处理函数
    function handleCopy(e) {
        setTimeout(() => {
            const selectedText = window.getSelection().toString();
            if (selectedText && selectedText.trim() !== '') {
                saveNote(selectedText);
            }
        }, 100);
    }
})();