Notebook Utility Panel

Utility panel with notebook-style notes (paging), dark/focus toggles, rich text, close ability

当前为 2025-10-01 提交的版本,查看 最新版本

// ==UserScript==
// @name         Notebook Utility Panel
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Utility panel with notebook-style notes (paging), dark/focus toggles, rich text, close ability
// @match        *://*/*
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    // --- STYLES ---
    GM_addStyle(`
      #tm-utility-btn {
        position: fixed; top: 50%; right: 0;
        transform: translateY(-50%);
        background: rgba(30,30,30,0.9);
        color: white; border: none;
        padding: 10px 14px;
        border-radius: 12px 0 0 12px;
        cursor: pointer;
        z-index: 999999;
        transition: background 0.3s ease, right 0.3s ease;
      }
      #tm-utility-btn:hover { background: rgba(50,50,50,0.95); }

      #tm-utility-panel {
        position: fixed; top: 50%; right: -380px;
        transform: translateY(-50%);
        width: 360px; height: 500px;
        background: rgba(250,250,250,0.98);
        box-shadow: -4px 0 20px rgba(0,0,0,0.2);
        border-radius: 12px 0 0 12px;
        display: flex; flex-direction: column;
        transition: right 0.4s ease;
        z-index: 999999;
        font-family: sans-serif;
      }
      #tm-utility-panel.open { right: 0; }

      #tm-controls {
        display: flex; gap: 6px;
        padding: 8px;
        background: #f0f0f0;
        border-bottom: 1px solid #ddd;
      }
      #tm-controls button {
        flex: 1;
        background: #fff;
        border: 1px solid #ccc;
        border-radius: 6px;
        padding: 4px 6px;
        cursor: pointer;
        font-size: 13px;
        transition: background 0.2s;
      }
      #tm-controls button:hover { background: #eee; }

      #tm-editor {
        flex: 1; padding: 12px;
        border: none; outline: none;
        font-size: 16px;
        overflow-y: auto;
        color: black;
        background: #fff;
        font-family: "Comic Sans MS", cursive, sans-serif;
      }

      .tm-dark .tm-editor {
        color: white !important;
        background: #222 !important;
      }

      .tm-focus aside, .tm-focus header, .tm-focus footer,
      .tm-focus nav, .tm-focus [class*="sidebar"], .tm-focus [id*="sidebar"] {
        display: none !important;
      }

      #tm-actions {
        display: flex; gap: 6px;
        padding: 8px;
        border-top: 1px solid #ddd;
        background: #fafafa;
      }
      #tm-actions button {
        flex: 1;
        background: #0078d7; color: white;
        border: none; border-radius: 6px;
        padding: 6px 8px;
        cursor: pointer;
        font-size: 13px;
        transition: background 0.2s;
      }
      #tm-actions button:hover { background: #005ea6; }

      #tm-paging {
        display: flex; justify-content: space-between;
        padding: 6px 8px;
        background: #f8f8f8;
        border-top: 1px solid #ddd;
      }
      #tm-paging button {
        background: #fff;
        border: 1px solid #aaa;
        border-radius: 6px;
        padding: 4px 6px;
        cursor: pointer;
        font-size: 13px;
        transition: background 0.2s;
      }
      #tm-paging button:hover { background: #eee; }

      #tm-close {
        position: absolute; top: 6px; right: 6px;
        background: transparent; border: none;
        font-size: 18px; cursor: pointer;
      }
    `);

    // --- PANEL + BUTTON ---
    const btn = document.createElement("button");
    btn.id = "tm-utility-btn";
    btn.textContent = "☰ Notes";
    document.body.appendChild(btn);

    const panel = document.createElement("div");
    panel.id = "tm-utility-panel";
    panel.innerHTML = `
      <button id="tm-close">✖️</button>
      <div id="tm-controls">
        <button id="bold">B</button>
        <button id="underline">U</button>
        <button id="link">🔗</button>
        <button id="dark">🌙</button>
        <button id="focus">🧘</button>
      </div>
      <div id="tm-editor" contenteditable="true"></div>
      <div id="tm-paging">
        <button id="prev-page">« Prev</button>
        <button id="next-page">Next »</button>
      </div>
      <div id="tm-actions">
        <button id="save">Save</button>
        <button id="open">Open</button>
        <button id="clear">Delete</button>
      </div>
    `;
    document.body.appendChild(panel);

    const editor = panel.querySelector("#tm-editor");
    const closeBtn = panel.querySelector("#tm-close");

    // Notebook data structure in localStorage
    const STORAGE_KEY = "tm-notebook-pages";
    let pages = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
    if (pages.length === 0) {
      pages.push("");  // start with one blank page
    }
    let currentPage = 0;

    // Load current page content
    function loadPage() {
      editor.innerHTML = pages[currentPage] || "";
    }

    // Save current page content
    function savePage() {
      pages[currentPage] = editor.innerHTML;
      localStorage.setItem(STORAGE_KEY, JSON.stringify(pages));
    }

    // Move to next page
    panel.querySelector("#next-page").onclick = () => {
      savePage();
      if (currentPage === pages.length - 1) {
        // add new page
        pages.push("");
      }
      currentPage++;
      loadPage();
    };

    // Move to prev page
    panel.querySelector("#prev-page").onclick = () => {
      savePage();
      if (currentPage > 0) {
        currentPage--;
      }
      loadPage();
    };

    // Load initial page
    loadPage();

    // --- TOGGLE PANEL ---
    btn.onclick = () => {
      panel.classList.toggle("open");
    };

    // Close panel
    closeBtn.onclick = () => {
      panel.classList.remove("open");
    };

    // --- FORMAT BUTTONS ---
    panel.querySelector("#bold").onclick = () => document.execCommand("bold");
    panel.querySelector("#underline").onclick = () => document.execCommand("underline");
    panel.querySelector("#link").onclick = () => {
      const url = prompt("Enter link URL:");
      if (url) document.execCommand("createLink", false, url);
    };

    // --- DARK MODE ---
    panel.querySelector("#dark").onclick = () => {
      document.body.classList.toggle("tm-dark");
    };

    // --- FOCUS MODE ---
    panel.querySelector("#focus").onclick = () => {
      document.body.classList.toggle("tm-focus");
    };

    // --- NOTE ACTIONS ---
    panel.querySelector("#save").onclick = () => {
      savePage();
      alert("Current page saved ✅");
    };

    panel.querySelector("#open").onclick = () => {
      const all = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
      if (all.length > 0) {
        pages = all;
        currentPage = 0;
        loadPage();
      }
    };

    panel.querySelector("#clear").onclick = () => {
      if (confirm("Delete all pages?")) {
        pages = [""];
        currentPage = 0;
        localStorage.setItem(STORAGE_KEY, JSON.stringify(pages));
        loadPage();
      }
    };

    // Before unload save current page
    window.addEventListener("beforeunload", () => {
      savePage();
      localStorage.setItem(STORAGE_KEY, JSON.stringify(pages));
    });

})();