// ==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);
}
})();