// ==UserScript== // @name 磁吸笔记 // @namespace https://bbs.tampermonkey.net.cn/ // @version 0.3.0 // @description 右侧侧吸笔记卡片,可新建和切换,内容自动保存;初始收起,标签可删除。 // @author huanxue // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_deleteValue // ==/UserScript== (() => { 'use strict'; const STORAGE_META = 'notes_meta'; const NOTE_PREFIX = 'note_'; const getMeta = () => JSON.parse(GM_getValue(STORAGE_META, '[]')); const saveMeta = m => GM_setValue(STORAGE_META, JSON.stringify(m)); const getContent = id => GM_getValue(NOTE_PREFIX + id, ''); const saveContent = (id, c) => GM_setValue(NOTE_PREFIX + id, c); const delContent = id => GM_deleteValue(NOTE_PREFIX + id); const debounce = (fn, d = 300) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), d); }; }; GM_addStyle(` #noteSidebar { position: fixed; top: 50px; right: 0; width: 300px; background: #fafafa; border-left: 1px solid #d0d0d0; box-shadow: -2px 0 6px rgba(0,0,0,.1); font-family: system-ui, sans-serif; z-index: 99999; transition: width .2s ease; } #noteSidebar.minimized { width: 40px; } #noteSidebar header { display: flex; align-items: center; gap: 6px; padding: 6px 8px; background: #f0f0f0; border-bottom: 1px solid #d0d0d0; } #noteSidebar header button { padding: 4px 6px; font-size: 12px; cursor: pointer; } #noteSidebar.minimized header span, #noteSidebar.minimized #btnNew, #noteSidebar.minimized #noteTabs, #noteSidebar.minimized #noteEditor { display: none !important; } #noteTabs { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px; max-height: 120px; overflow-y: auto; } .note-tab { position: relative; cursor: pointer; background: #e0e0e0; border-radius: 4px; padding: 4px 18px 4px 6px; font-size: 12px; user-select: none; } .note-tab.active { background: #c0c0c0; font-weight: 600; } .note-close { position: absolute; right: 4px; top: 1px; cursor: pointer; font-weight: 700; line-height: 12px; } .note-close:hover { color: #d00; } #noteEditor { width: 100%; height: 200px; border: none; resize: vertical; padding: 8px; box-sizing: border-box; font-size: 13px; line-height: 1.4; border-top: 1px solid #d0d0d0; background: #fff; } `); const sidebar = document.createElement('div'); sidebar.id = 'noteSidebar'; sidebar.classList.add('minimized'); sidebar.innerHTML = `
Notes
`; document.body.appendChild(sidebar); const btnToggle = sidebar.querySelector('#btnToggle'); const btnNew = sidebar.querySelector('#btnNew'); const noteTabs = sidebar.querySelector('#noteTabs'); const noteEditor = sidebar.querySelector('#noteEditor'); let currentId = null; const deleteNote = id => { let meta = getMeta().filter(m => m.id !== id); saveMeta(meta); delContent(id); if (currentId === id) { if (meta.length) { openNote(meta[0].id); } else { const nid = Date.now().toString(); meta = [{ id: nid, name: 'Note 1' }]; saveMeta(meta); openNote(nid); } } else { renderTabs(); } }; const renderTabs = () => { noteTabs.innerHTML = ''; getMeta().forEach(({ id, name }) => { const tab = document.createElement('div'); tab.className = 'note-tab' + (id === currentId ? ' active' : ''); tab.innerHTML = ` ${name} `; tab.onclick = () => openNote(id); tab.querySelector('.note-close').onclick = e => { e.stopPropagation(); deleteNote(id); }; noteTabs.appendChild(tab); }); }; const openNote = id => { currentId = id; renderTabs(); noteEditor.value = getContent(id); }; btnNew.onclick = () => { const nameInput = prompt('新建卡片名称?', 'Note ' + (getMeta().length + 1)); if (!nameInput) return; const name = nameInput.trim(); if (!name) return; const meta = getMeta(); const id = Date.now().toString(); meta.push({ id, name }); saveMeta(meta); openNote(id); }; noteEditor.addEventListener('input', debounce(() => { if (currentId) saveContent(currentId, noteEditor.value); }, 500)); btnToggle.onclick = () => { sidebar.classList.toggle('minimized'); const minimized = sidebar.classList.contains('minimized'); btnToggle.textContent = minimized ? '☝️' : '👐'; btnToggle.title = minimized ? '展开' : '收起'; }; (() => { let meta = getMeta(); if (!meta.length) { const id = Date.now().toString(); meta = [{ id, name: 'Note 1' }]; saveMeta(meta); } openNote(meta[0].id); })(); })();