Character.ai Text Highlighter and Note Taker

Advanced text highlighting and note-taking for Character.ai with single-click and un-highlighting.

// ==UserScript==
// @name         Character.ai Text Highlighter and Note Taker
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Advanced text highlighting and note-taking for Character.ai with single-click and un-highlighting.
// @match        https://character.ai/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // Enhanced styling for highlights and notes
    const styleElement = document.createElement('style');
    styleElement.textContent = `
        .ca-highlight {
            background-color: yellow;
            color: black;
            cursor: pointer;
            transition: background-color 0.2s ease;
            display: inline; /* Ensure inline display for proper spacing */
            white-space: pre-wrap; /* Preserve whitespace */
        }
        .ca-highlight:hover {
            background-color: #ffff99;
        }
        .ca-note-popup {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #1a1a1a;
            border: 2px solid #333;
            border-radius: 8px;
            padding: 20px;
            width: 350px;
            z-index: 10000;
            box-shadow: 0 4px 6px rgba(0,0,0,0.5);
        }
        .ca-note-input {
            width: 100%;
            margin-bottom: 10px;
            padding: 8px;
            background-color: #2a2a2a;
            color: white;
            border: 1px solid #444;
            border-radius: 4px;
            resize: vertical;
            min-height: 100px;
        }
        .ca-note-buttons {
            display: flex;
            justify-content: space-between;
        }
        .ca-note-save {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 10px;
        }
        .ca-note-cancel {
            background-color: #f44336;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
        }
        .ca-highlight-mode-indicator {
            position: fixed;
            top: 10px;
            right: 10px;
            background-color: yellow;
            color: black;
            padding: 5px 10px;
            border-radius: 4px;
            z-index: 9999;
            display: none;
            font-weight: bold;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .ca-note-button, .ca-unhighlight-button {
            margin-right: 10px;
            background-color: #555;
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 4px;
            cursor: pointer;
        }
        .ca-unhighlight-button {
            background-color: #ff9800;
        }
        .ca-popup-menu {
            position: absolute;
            background-color: #1a1a1a;
            border: 1px solid #333;
            border-radius: 4px;
            padding: 5px;
            z-index: 10001;
            box-shadow: 0 2px 4px rgba(0,0,0,0.3);
        }
    `;
    document.head.appendChild(styleElement);

    // Create highlight mode indicator
    const highlightModeIndicator = document.createElement('div');
    highlightModeIndicator.classList.add('ca-highlight-mode-indicator');
    highlightModeIndicator.textContent = 'Highlight Mode';
    document.body.appendChild(highlightModeIndicator);

    // State variables
    let isHighlightMode = false;
    const STORAGE_KEY = 'caNotes';

    // Utility function to get unique identifier for a word
    function getWordIdentifier(word) {
        return `highlight_${btoa(word.trim())}`;
    }

    // Save note for a word
    function saveNote(word, note) {
        const identifier = getWordIdentifier(word);
        const savedNotes = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
        savedNotes[identifier] = note;
        localStorage.setItem(STORAGE_KEY, JSON.stringify(savedNotes));
    }

    // Retrieve note for a word
    function getNote(word) {
        const identifier = getWordIdentifier(word);
        const savedNotes = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
        return savedNotes[identifier] || '';
    }

    // Create note popup
    function createNotePopup(word) {
        const popup = document.createElement('div');
        popup.classList.add('ca-note-popup');

        const textarea = document.createElement('textarea');
        textarea.classList.add('ca-note-input');
        textarea.placeholder = `Enter note for "${word}"`;
        textarea.value = getNote(word);

        const buttonContainer = document.createElement('div');
        buttonContainer.classList.add('ca-note-buttons');

        const saveButton = document.createElement('button');
        saveButton.textContent = 'Save';
        saveButton.classList.add('ca-note-save');
        saveButton.addEventListener('click', () => {
            saveNote(word, textarea.value);
            document.body.removeChild(popup);
        });

        const cancelButton = document.createElement('button');
        cancelButton.textContent = 'Discard';
        cancelButton.classList.add('ca-note-cancel');
        cancelButton.addEventListener('click', () => {
            document.body.removeChild(popup);
        });

        buttonContainer.appendChild(saveButton);
        buttonContainer.appendChild(cancelButton);

        popup.appendChild(textarea);
        popup.appendChild(buttonContainer);

        return popup;
    }

    // New function to un-highlight text
    function unhighlightText(element) {
        if (element && element.classList.contains('ca-highlight')) {
            // Get the text content
            const text = element.textContent;

            // Create a text node to replace the highlighted span
            const textNode = document.createTextNode(text);

            // Replace the span with the text node
            element.parentNode.replaceChild(textNode, element);
        }
    }

    // Handle highlighting and un-highlighting with a single click
    function handleHighlightClick(e) {
        // Only work in highlight mode
        if (!isHighlightMode) return;

        // Check if clicked on an existing highlight
        if (e.target.classList && e.target.classList.contains('ca-highlight')) {
            // Un-highlight the text
            unhighlightText(e.target);
            return;
        }

        // Otherwise, create a new highlight
        const selection = window.getSelection();
        const selectedText = selection.toString();

        if (selectedText && selectedText.trim() !== '') {
            try {
                // Get the range
                const range = selection.getRangeAt(0);

                // Store the original text with spaces for note-taking
                const originalText = selectedText;

                // Create a highlight span
                const highlightSpan = document.createElement('span');
                highlightSpan.classList.add('ca-highlight');

                // Use createTextNode to preserve spaces
                highlightSpan.appendChild(document.createTextNode(selectedText));

                // Replace selection with our highlight
                range.deleteContents();
                range.insertNode(highlightSpan);

                // Clear the selection
                selection.removeAllRanges();
            } catch (error) {
                console.error('Error highlighting text:', error);
            }
        }
    }

    // Add right-click handling for highlights
    function handleHighlightRightClick(e) {
        // Check if right-clicked on a highlight
        if (e.target.classList && e.target.classList.contains('ca-highlight')) {
            e.preventDefault();

            // Get the highlighted text
            const highlightedText = e.target.textContent;

            // Create and display a popup menu for the note
            const notePopup = createNotePopup(highlightedText);
            document.body.appendChild(notePopup);
        } else {
            // Add note functionality when not in highlight mode
            if (!isHighlightMode) {
                const selection = window.getSelection();
                const selectedText = selection.toString();

                if (selectedText && selectedText.trim() !== '') {
                    e.preventDefault();

                    // Create and display a popup menu for the note
                    const notePopup = createNotePopup(selectedText);
                    document.body.appendChild(notePopup);
                }
            }
        }
    }

    // Toggle highlight mode
    function toggleHighlightMode() {
        isHighlightMode = !isHighlightMode;
        highlightModeIndicator.style.display = isHighlightMode ? 'block' : 'none';

        // Update the indicator text to show the new mode behavior
        highlightModeIndicator.textContent = isHighlightMode ?
            'Highlight Mode (Click to highlight/unhighlight, Right-click for notes)' :
            'Highlight Mode';
    }

    // Event listeners
    document.addEventListener('click', (e) => {
        // Single click for highlighting/unhighlighting when in highlight mode
        handleHighlightClick(e);
    });

    document.addEventListener('contextmenu', (e) => {
        // Right-click for adding notes to highlights
        handleHighlightRightClick(e);
    });

    document.addEventListener('keydown', (e) => {
        // Toggle highlight mode with '/'
        if (e.key === '/') {
            e.preventDefault();
            toggleHighlightMode();
        }
    });

    // Add initial message about feature
    console.log('Character.ai Text Highlighter Loaded:\n- Highlight Mode: "/" to toggle\n- While in highlight mode: click to highlight/unhighlight\n- Right-click on highlighted text to add/view notes\n- Right-click on selected text to add notes when not in highlight mode');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址