Professional Website Notes Manager

Professional notes manager with editable URLs, modern interface, and quick delete functionality

目前为 2025-02-23 提交的版本。查看 最新版本

// ==UserScript==
// @name         Professional Website Notes Manager
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  Professional notes manager with editable URLs, modern interface, and quick delete functionality
// @author       Byakuran
// @match        https://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    let scriptVersion = '0.7'

    const defaultOptions = {
        version: scriptVersion,
        darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches,
        addTimestampToTitle: false,
        showUrlLinksInNotesList: true,
        shortcuts: {
            newNote: { ctrlKey: true, shiftKey: true, key: 'S' },
            currentPageNotes: { ctrlKey: true, shiftKey: true, key: 'C' },
            allNotes: { ctrlKey: true, shiftKey: true, key: 'L' },
            showOptions: { ctrlKey: true, altKey: true, key: 'O' }
        }
    };

    let options = checkAndUpdateOptions();
    GM_setValue('options', options);

    function checkAndUpdateOptions() {
        let currentOptions = GM_getValue('options', defaultOptions);

        // Check if the version has changed or if it doesn't exist
        if (!currentOptions.version || currentOptions.version !== defaultOptions.version) {
            // Version has changed, update options
            for (let key in defaultOptions) {
                if (!(key in currentOptions)) {
                    currentOptions[key] = defaultOptions[key];
                }
            }

            // Update nested objects (shortcuts, possibly more later)
            for (let key in defaultOptions.shortcuts) {
                if (!(key in currentOptions.shortcuts)) {
                    currentOptions.shortcuts[key] = defaultOptions.shortcuts[key];
                }
            }

            // Update the version
            currentOptions.version = defaultOptions.version;

            // Save the updated options
            GM_setValue('options', currentOptions);

            alert('Options updated to version ' + defaultOptions.version);
            console.log('Options updated to version ' + defaultOptions.version);
        }

        return currentOptions;
    }

    const isDarkMode = options.darkMode;

    const darkModeStyles = {
        modal: {
            bg: '#1f2937',
            text: '#f3f4f6'
        },
        input: {
            bg: '#374151',
            border: '#4b5563',
            text: '#f3f4f6'
        },
        button: {
            primary: '#3b82f6',
            primaryHover: '#2563eb',
            secondary: '#4b5563',
            secondaryHover: '#374151',
            text: '#ffffff'
        },
        listItem: {
            bg: '#374151',
            bgHover: '#4b5563',
            text: '#f3f4f6'
        }
    };

    const lightModeStyles = {
        modal: {
            bg: '#ffffff',
            text: '#111827'
        },
        input: {
            bg: '#f9fafb',
            border: '#e5e7eb',
            text: '#111827'
        },
        button: {
            primary: '#3b82f6',
            primaryHover: '#2563eb',
            secondary: '#f3f4f6',
            secondaryHover: '#e5e7eb',
            text: '#ffffff'
        },
        listItem: {
            bg: '#ffffff',
            bgHover: '#f9fafb',
            text: '#1f2937'
        }
    };

    const currentTheme = isDarkMode ? darkModeStyles : lightModeStyles;

    const styles = `
        .notes-overlay .notes-modal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: ${currentTheme.modal.bg};
            color: ${currentTheme.modal.text};
            padding: 32px;
            border-radius: 16px;
            box-shadow: 0 8px 32px rgba(0,0,0,0.25);
            z-index: 10000;
            max-width: 700px;
            width: 90%;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        }
        .notes-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,${isDarkMode ? '0.8' : '0.7'});
            z-index: 9999;
            backdrop-filter: blur(4px);
        }
        .notes-overlay .notes-input {
            width: 100%;
            margin: 12px 0;
            padding: 12px 16px;
            border: 2px solid ${currentTheme.input.border};
            border-radius: 8px;
            font-size: 15px;
            transition: all 0.2s ease;
            background: ${currentTheme.input.bg};
            color: ${currentTheme.input.text};
            box-sizing: border-box;
        }
        .notes-overlay .notes-input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }
        .notes-overlay .notes-textarea {
            width: 100%;
            height: 200px;
            margin: 12px 0;
            padding: 16px;
            border: 2px solid ${currentTheme.input.border};
            border-radius: 8px;
            font-size: 15px;
            resize: vertical;
            transition: all 0.2s ease;
            background: ${currentTheme.input.bg};
            color: ${currentTheme.input.text};
            line-height: 1.5;
            box-sizing: border-box;
        }
        .notes-overlay .notes-textarea:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }
        .notes-overlay .notes-button {
            background: ${currentTheme.button.primary};
            color: ${currentTheme.button.text};
            border: none;
            padding: 12px 24px;
            border-radius: 8px;
            cursor: pointer;
            margin: 5px;
            font-size: 15px;
            font-weight: 500;
            transition: all 0.2s ease;
        }
        .notes-overlay .notes-button:hover {
            background: ${currentTheme.button.primaryHover};
            transform: translateY(-1px);
        }
        .notes-overlay .notes-button.secondary {
            background: ${currentTheme.button.secondary};
            color: ${isDarkMode ? '#f3f4f6' : '#4b5563'};
        }
        .notes-overlay .notes-button.secondary:hover {
            background: ${currentTheme.button.secondaryHover};
        }
        .notes-overlay .notes-button.delete {
            background: #ef4444;
        }
        .notes-overlayt .notes-button.delete:hover {
            background: #dc2626;
        }
        .notes-overlay .notes-button.edit {
            background: #10b981;
        }
        .notes-overlay .notes-button.edit:hover {
            background: #059669;
        }
        .notes-overlay .notes-list-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 16px;
            border: 1px solid ${currentTheme.input.border};
            border-radius: 8px;
            margin: 8px 0;
            cursor: pointer;
            transition: all 0.2s ease;
            background: ${currentTheme.listItem.bg};
            color: ${currentTheme.listItem.text};
        }
        .notes-overlay .notes-list-item:hover {
            background: ${currentTheme.listItem.bgHover};
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(0,0,0,${isDarkMode ? '0.3' : '0.05'});
        }
        .notes-overlay .close-button {
            position: absolute;
            top: 16px;
            right: 16px;
            cursor: pointer;
            font-size: 24px;
            color: ${isDarkMode ? '#9ca3af' : '#6b7280'};
            transition: all 0.2s;
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
        }
        .notes-overlay .close-button:hover {
            color: ${isDarkMode ? '#f3f4f6' : '#111827'};
            background: ${isDarkMode ? '#374151' : '#f3f4f6'};
        }
        .notes-overlay .modal-title {
            font-size: 20px;
            font-weight: 600;
            margin-bottom: 24px;
            color: ${currentTheme.modal.text};
        }
        .notes-overlay .url-text {
            font-size: 14px;
            color: ${isDarkMode ? '#9ca3af' : '#6b7280'};
            word-break: break-all;
            margin-bottom: 16px;
            padding: 8px 12px;
            background: ${isDarkMode ? '#374151' : '#f3f4f6'};
            border-radius: 6px;
        }
        .notes-overlay .timestamp {
            font-size: 12px;
            color: ${isDarkMode ? '#9ca3af' : '#6b7280'};
            margin-top: 4px;
        }
        .notes-overlay .delete-note-button {
            background: none;
            border: none;
            color: #ef4444;
            font-size: 18px;
            cursor: pointer;
            padding: 4px 8px;
            border-radius: 4px;
            transition: all 0.2s ease;
        }
        .notes-overlay .delete-note-button:hover {
            background: #ef4444;
            color: #ffffff;
        }
        .notes-overlay .notes-options-input {
            width: 100%;
            margin: 8px 0;
            padding: 10px 14px;
            border: 2px solid ${currentTheme.input.border};
            border-radius: 8px;
            font-size: 15px;
            transition: all 0.2s ease;
            background: ${currentTheme.input.bg};
            color: ${currentTheme.input.text};
            box-sizing: border-box;
        }

        .notes-overlay .notes-options-input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
        }

        .notes-overlay .notes-options-checkbox {
            margin-right: 8px;
        }

        .notes-overlay .notes-options-label {
            display: flex;
            align-items: center;
            margin: 10px 0;
            color: ${currentTheme.modal.text};
        }
    `;

    const styleSheet = document.createElement("style");
    styleSheet.innerText = styles;
    document.head.appendChild(styleSheet);

    function showOptionsMenu() {
        const container = document.createElement('div');
        container.innerHTML = `
            <h3 class="modal-title">Options</h3>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="darkModeToggle" ${options.darkMode ? 'checked' : ''}>
                    Dark Mode
                </label>
            </div>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="timestampToggle" ${options.addTimestampToTitle ? 'checked' : ''}>
                    Add timestamp to note titles
                </label>
            </div>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="showUrlLinksToggle" ${options.showUrlLinksInNotesList ? 'checked' : ''}>
                    Show URL links in notes list
                </label>
            </div>
            <h4 style="margin-top: 20px;">Keyboard Shortcuts</h4>
            <div>
                <label>New Note:
                    <input type="text" class="notes-options-input" id="newNoteShortcut" value="${getShortcutString(options.shortcuts.newNote)}">
                </label>
            </div>
            <div>
                <label>Current Page Notes:
                    <input type="text" class="notes-options-input" id="currentPageNotesShortcut" value="${getShortcutString(options.shortcuts.currentPageNotes)}">
                </label>
            </div>
            <div>
                <label>All Notes:
                    <input type="text" class="notes-options-input" id="allNotesShortcut" value="${getShortcutString(options.shortcuts.allNotes)}">
                </label>
            </div>
            <div>
                <label>Show Options:
                    <input type="text" class="notes-options-input" id="showOptionsWindow" value="${getShortcutString(options.shortcuts.showOptions)}">
                </label>
            </div>
            <button id="saveOptions" class="notes-button" style="margin-top: 20px;">Save Options</button>
        `;

        createModal(container);
        document.getElementById('saveOptions').onclick = saveOptions;
    }

    function getShortcutString(shortcut) {
        let str = '';
        if (shortcut.ctrlKey) str += 'Ctrl+';
        if (shortcut.shiftKey) str += 'Shift+';
        if (shortcut.altKey) str += 'Alt+';
        str += shortcut.key.toUpperCase();
        return str;
    }

    function parseShortcutString(str) {
        const parts = str.toLowerCase().split('+');
        return {
            ctrlKey: parts.includes('ctrl'),
            shiftKey: parts.includes('shift'),
            altKey: parts.includes('alt'),
            key: parts[parts.length - 1]
        };
    }

    function saveOptions() {
        options = {
            version: scriptVersion,
            darkMode: document.getElementById('darkModeToggle').checked,
            addTimestampToTitle: document.getElementById('timestampToggle').checked,
            showUrlLinksInNotesList: document.getElementById('showUrlLinksToggle').checked,
            shortcuts: {
                newNote: parseShortcutString(document.getElementById('newNoteShortcut').value),
                currentPageNotes: parseShortcutString(document.getElementById('currentPageNotesShortcut').value),
                allNotes: parseShortcutString(document.getElementById('allNotesShortcut').value),
                showOptions: parseShortcutString(document.getElementById('showOptionsWindow').value)
            }
        };
        GM_setValue('options', options);
        setupShortcutListener();
        alert('Options saved. Changes have been applied, some changes may require reloading.');
    }


    GM_registerMenuCommand('Toggle Dark Mode', () => {
        const newMode = !isDarkMode;
        GM_setValue('darkMode', newMode);
        location.reload();
    });

    function createModal(content) {
        const overlay = document.createElement('div');
        overlay.className = 'notes-overlay';

        const modal = document.createElement('div');
        modal.className = 'notes-modal';

        const closeButton = document.createElement('span');
        closeButton.className = 'close-button';
        closeButton.textContent = '×';
        closeButton.onclick = () => overlay.remove();

        modal.appendChild(closeButton);
        modal.appendChild(content);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        const escapeListener = (e) => {
            if (e.key === 'Escape') {
                overlay.remove();
                document.removeEventListener('keydown', escapeListener);
            }
        };
        document.addEventListener('keydown', escapeListener);
    }

    function getAllNotes() {
        return GM_getValue('website-notes', {});
    }

    function saveNote(title, url, content, timestamp = Date.now(), pinned = false) {
        const notes = getAllNotes();
        if (!notes[url]) notes[url] = [];

        // Add timestamp to title if the option is enabled
        let finalTitle = title;
        if (options.addTimestampToTitle) {
            const date = new Date(timestamp);
            const formattedDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
            finalTitle = `${title} [${formattedDate}]`;
        }

        notes[url].push({ title: finalTitle, content, timestamp, pinned });
        GM_setValue('website-notes', notes);
    }

    function updateNote(oldUrl, index, title, newUrl, content, pinned) {
        const notes = getAllNotes();
        deleteNote(oldUrl, index);
        saveNote(title, newUrl, content, notes[oldUrl][index].timestamp, pinned);
    }

    function togglePinNote(url, index) {
        const notes = getAllNotes();
        if (notes[url] && notes[url][index]) {
            notes[url][index].pinned = !notes[url][index].pinned;
            GM_setValue('website-notes', notes);
        }
    }

    function deleteNote(url, index) {
        const notes = getAllNotes();
        if (notes[url]) {
            notes[url].splice(index, 1);
            if (notes[url].length === 0) delete notes[url];
            GM_setValue('website-notes', notes);
        }
    }

    function showNoteForm(editMode = false, existingNote = null, url = null, index = null, pinned = false) {
        const container = document.createElement('div');
        container.innerHTML = `<h3 class="modal-title">${editMode ? 'Edit Note' : 'Create New Note'}</h3>`;

        const titleInput = document.createElement('input');
        titleInput.className = 'notes-input';
        titleInput.placeholder = 'Enter title';
        titleInput.value = editMode ? existingNote.title : '';

        const urlInput = document.createElement('input');
        urlInput.className = 'notes-input';
        urlInput.placeholder = 'Enter URL(s) or URL pattern(s), separated by spaces (e.g., https://domain.com/*)';
        urlInput.value = editMode ? url : window.location.href;

        const patternHelp = document.createElement('div');
        patternHelp.style.fontSize = '12px';
        patternHelp.style.color = isDarkMode ? '#9ca3af' : '#6b7280';
        patternHelp.style.marginTop = '-8px';
        patternHelp.style.marginBottom = '8px';
        patternHelp.innerHTML = 'Use * for wildcard matching. Multiple URLs: separate with spaces (e.g., https://domain1.com/* https://domain2.com/*)';

        const contentArea = document.createElement('textarea');
        contentArea.className = 'notes-textarea';
        contentArea.placeholder = 'Enter your notes here';
        contentArea.value = editMode ? existingNote.content : '';

        const buttonGroup = document.createElement('div');
        buttonGroup.className = 'button-group';

        const saveButton = document.createElement('button');
        saveButton.className = 'notes-button';
        saveButton.textContent = editMode ? 'Update Note' : 'Save Note';
        saveButton.onclick = () => {
            if (titleInput.value && contentArea.value) {
                if (editMode) {
                    updateNote(url, index, titleInput.value, urlInput.value, contentArea.value, existingNote.pinned);
                } else {
                    saveNote(titleInput.value, urlInput.value, contentArea.value);
                }
                container.parentElement.parentElement.remove();
                showCurrentPageNotes();
            }
        };

        const cancelButton = document.createElement('button');
        cancelButton.className = 'notes-button secondary';
        cancelButton.textContent = 'Cancel';
        cancelButton.onclick = () => container.parentElement.parentElement.remove();

        buttonGroup.appendChild(saveButton);
        buttonGroup.appendChild(cancelButton);

        container.appendChild(titleInput);
        container.appendChild(urlInput);
        container.appendChild(patternHelp);
        container.appendChild(contentArea);
        container.appendChild(buttonGroup);

        createModal(container);
    }

    function formatDate(timestamp) {
        return new Date(timestamp).toLocaleString();
    }

    function showNoteContent(note, url, index) {
        const container = document.createElement('div');

        // Function to convert URLs to clickable links and preserve line breaks
        function linkify(text) {
            // URL pattern for matching
            const urlPattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;

            // Replace URLs with anchor tags
            const linkedText = text.replace(urlPattern, function(url) {
                return `<a href="${url}" target="_blank" style="color: #3b82f6; text-decoration: underline; word-break: break-all;" onclick="event.stopPropagation();">${url}</a>`;
            });

            // Convert line breaks to <br> tags and maintain whitespace
            return linkedText.replace(/\n/g, '<br>').replace(/\s{2,}/g, function(space) {
                return ' ' + '&nbsp;'.repeat(space.length - 1);
            });
        }

        // Create a hidden textarea for proper copying
        const hiddenTextarea = document.createElement('textarea');
        hiddenTextarea.style.position = 'absolute';
        hiddenTextarea.style.left = '-9999px';
        hiddenTextarea.style.top = '-9999px';
        document.body.appendChild(hiddenTextarea);

        container.innerHTML = `
            <h3 class="modal-title">${note.title}</h3>
            <div class="url-text">${url}</div>
            <div class="timestamp">Created: ${formatDate(note.timestamp)}</div>
            <div style="margin: 16px 0; line-height: 1.6;" class="note-content">${linkify(note.content)}</div>
        `;

        // Add copy event listener to the content div
        const contentDiv = container.querySelector('.note-content');
        contentDiv.addEventListener('copy', (e) => {
            e.preventDefault();
            const selection = window.getSelection();
            const selectedText = selection.toString();

            // Replace <br> tags with actual newlines in the copied text
            hiddenTextarea.value = selectedText.replace(/\s*\n\s*/g, '\n');
            hiddenTextarea.select();
            document.execCommand('copy');

            // Clean up
            selection.removeAllRanges();
            selection.addRange(document.createRange());
        });

        const buttonGroup = document.createElement('div');
        buttonGroup.className = 'button-group';

        const editButton = document.createElement('button');
        editButton.className = 'notes-button edit';
        editButton.textContent = 'Edit';
        editButton.onclick = () => {
            container.parentElement.parentElement.remove();
            showNoteForm(true, note, url, index);
        };

        const deleteButton = document.createElement('button');
        deleteButton.className = 'notes-button delete';
        deleteButton.textContent = 'Delete';
        deleteButton.onclick = () => {
            if (confirm('Are you sure you want to delete this note?')) {
                deleteNote(url, index);
                container.parentElement.parentElement.remove();
                showCurrentPageNotes();
            }
        };

        const pinButton = document.createElement('button');
        pinButton.className = `notes-button ${note.pinned ? 'secondary' : ''}`;
        pinButton.textContent = note.pinned ? 'Unpin' : 'Pin';
        pinButton.onclick = () => {
            togglePinNote(url, index);
            pinButton.textContent = note.pinned ? 'Pin' : 'Unpin';
            pinButton.className = `notes-button ${!note.pinned ? 'secondary' : ''}`;
            displayPinnedNotes();
        };

        buttonGroup.appendChild(editButton);
        buttonGroup.appendChild(deleteButton);
        buttonGroup.appendChild(pinButton);
        container.appendChild(buttonGroup);

        createModal(container);
    }

    function displayPinnedNotes() {
        const notes = getAllNotes();
        const currentUrl = window.location.href;
        let pinnedNotesContainer = document.getElementById('pinned-notes-container');

        if (!pinnedNotesContainer) {
            pinnedNotesContainer = document.createElement('div');
            pinnedNotesContainer.id = 'pinned-notes-container';
            pinnedNotesContainer.style.position = 'fixed';
            pinnedNotesContainer.style.top = '10px';
            pinnedNotesContainer.style.right = '10px';
            pinnedNotesContainer.style.zIndex = '9999';
            pinnedNotesContainer.style.maxWidth = '300px';
            document.body.appendChild(pinnedNotesContainer);
        }

        pinnedNotesContainer.innerHTML = '';

        for (const url in notes) {
            if (doesUrlMatchPattern(url, currentUrl)) {
                notes[url].forEach((note, index) => {
                    if (note.pinned) {
                        const noteDiv = document.createElement('div');
                        noteDiv.className = 'pinned-note';
                        noteDiv.style.background = currentTheme.listItem.bg;
                        noteDiv.style.color = currentTheme.listItem.text;
                        noteDiv.style.padding = '10px';
                        noteDiv.style.margin = '5px 0';
                        noteDiv.style.borderRadius = '8px';
                        noteDiv.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
                        noteDiv.style.cursor = 'pointer';
                        noteDiv.innerHTML = `<strong>${note.title}</strong>`;
                        noteDiv.onclick = () => showNoteContent(note, url, index);
                        pinnedNotesContainer.appendChild(noteDiv);
                    }
                });
            }
        }
    }


    function doesUrlMatchPattern(urlPatterns, currentUrl) {
        // Split the pattern string into an array of patterns
        const patterns = urlPatterns.split(/\s+/).filter(pattern => pattern.trim() !== '');

        // Check if any of the patterns match the current URL
        return patterns.some(pattern => {
            // Escape special characters for regex
            const escapeRegex = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

            // Convert Tampermonkey-style pattern to regex
            const patternToRegex = (pattern) => {
                const parts = pattern.split('*');
                let regexString = '^';
                for (let i = 0; i < parts.length; i++) {
                    regexString += escapeRegex(parts[i]);
                    if (i < parts.length - 1) {
                        if (parts[i + 1] === '') {
                            // '**' matches any number of path segments
                            regexString += '.*';
                            i++; // Skip the next '*'
                        } else {
                            // Single '*' matches anything except '/'
                            regexString += '[^/]*';
                        }
                    }
                }
                // If the pattern ends with '**', allow anything at the end
                if (pattern.endsWith('**')) {
                    regexString += '.*';
                } else if (!pattern.endsWith('*')) {
                    regexString += '$';
                }
                return new RegExp(regexString);
            };

            try {
                const regex = patternToRegex(pattern);
                return regex.test(currentUrl);
            } catch (e) {
                console.error('Invalid URL pattern:', e);
                return false;
            }
        });
    }

    function showCurrentPageNotes() {
        const notes = getAllNotes();
        const currentUrl = window.location.href;
        let matchingNotes = [];

        // Collect all matching notes
        for (const urlPattern in notes) {
            if (doesUrlMatchPattern(urlPattern, currentUrl)) {
                matchingNotes.push({
                    pattern: urlPattern,
                    notes: notes[urlPattern]
                });
            }
        }

        const container = document.createElement('div');
        container.innerHTML = `
            <h3 class="modal-title">Notes for Current Page</h3>
            <div class="url-text">${currentUrl}</div>
        `;

        if (matchingNotes.length === 0) {
            container.innerHTML += '<p style="color: #6b7280;">No matching notes found for this page</p>';
        } else {
            matchingNotes.forEach(({pattern, notes: patternNotes}) => {
                const patternDiv = document.createElement('div');

                if (options.showUrlLinksInNotesList) {
                    patternDiv.innerHTML = `<div class="url-text">Pattern: ${pattern}</div>`;
                }
                patternNotes.forEach((note, index) => {
                    const noteDiv = document.createElement('div');
                    noteDiv.className = 'notes-list-item';
                    noteDiv.innerHTML = `
                        <span style="flex-grow: 1; display: flex; align-items: center;">${note.title}</span>
                        <button class="delete-note-button" title="Delete note">×</button>
                    `;

                    noteDiv.onclick = (e) => {
                        if (!e.target.classList.contains('delete-note-button')) {
                            container.parentElement.parentElement.remove();
                            showNoteContent(note, pattern, index);
                        }
                    };

                    noteDiv.querySelector('.delete-note-button').onclick = (e) => {
                        e.stopPropagation();
                        if (confirm('Are you sure you want to delete this note?')) {
                            deleteNote(pattern, index);
                            noteDiv.remove();
                            if (patternNotes.length === 1) {
                                patternDiv.remove();
                            }
                        }
                    };

                    patternDiv.appendChild(noteDiv);
                });

                container.appendChild(patternDiv);
            });
        }

        // Add help button and dropdown
        const helpButton = document.createElement('button');
        helpButton.textContent = '?';
        helpButton.style.position = 'absolute';
        helpButton.style.top = '16px';
        helpButton.style.right = '56px';
        helpButton.style.width = '32px';
        helpButton.style.height = '32px';
        helpButton.style.borderRadius = '50%';
        helpButton.style.border = 'none';
        helpButton.style.background = isDarkMode ? '#374151' : '#e5e7eb';
        helpButton.style.color = isDarkMode ? '#f3f4f6' : '#4b5563';
        helpButton.style.fontSize = '18px';
        helpButton.style.cursor = 'pointer';
        helpButton.style.display = 'flex';
        helpButton.style.alignItems = 'center';
        helpButton.style.justifyContent = 'center';
        helpButton.title = 'URL Pattern Help';

        const helpDropdown = document.createElement('div');
        helpDropdown.style.position = 'absolute';
        helpDropdown.style.top = '52px';
        helpDropdown.style.right = '56px';
        helpDropdown.style.background = isDarkMode ? '#1f2937' : '#ffffff';
        helpDropdown.style.border = `1px solid ${isDarkMode ? '#4b5563' : '#e5e7eb'}`;
        helpDropdown.style.borderRadius = '8px';
        helpDropdown.style.padding = '16px';
        helpDropdown.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)';
        helpDropdown.style.zIndex = '10001';
        helpDropdown.style.display = 'none';
        helpDropdown.style.maxWidth = '300px';
        helpDropdown.style.color = isDarkMode ? '#f3f4f6' : '#4b5563';
        helpDropdown.innerHTML = `
            <strong>URL Pattern Examples:</strong><br>
            - https://domain.com/* (matches entire domain, one level deep)<br>
            - https://domain.com/** (matches entire domain, any number of levels)<br>
            - https://domain.com/specific/* (matches specific path and one level below)<br>
            - https://domain.com/specific/** (matches specific path and any levels below)<br>
            - https://domain.com/*/specific (matches specific ending, one level in between)<br>
            - https://domain.com/**/specific (matches specific ending, any number of levels in between)
        `;

        let isDropdownOpen = false;

        helpButton.onmouseenter = () => {
            if (!isDropdownOpen) {
                helpDropdown.style.display = 'block';
            }
        };

        helpButton.onmouseleave = () => {
            if (!isDropdownOpen) {
                helpDropdown.style.display = 'none';
            }
        };

        helpButton.onclick = () => {
            isDropdownOpen = !isDropdownOpen;
            helpDropdown.style.display = isDropdownOpen ? 'block' : 'none';
        };

        document.addEventListener('click', (e) => {
            if (isDropdownOpen && e.target !== helpButton && !helpDropdown.contains(e.target)) {
                isDropdownOpen = false;
                helpDropdown.style.display = 'none';
            }
        });

        container.appendChild(helpButton);
        container.appendChild(helpDropdown);

        createModal(container);
    }

    function showAllNotes() {
        const notes = getAllNotes();
        const container = document.createElement('div');
        container.innerHTML = '<h3 class="modal-title">All Notes</h3>';

        if (Object.keys(notes).length === 0) {
            container.innerHTML += '<p style="color: #6b7280;">No notes found</p>';
        } else {
            for (const url in notes) {
                const urlDiv = document.createElement('div');
                urlDiv.innerHTML = `<div class="url-text">${url}</div>`;

                notes[url].forEach((note, index) => {
                    const noteDiv = document.createElement('div');
                    noteDiv.className = 'notes-list-item';
                    noteDiv.innerHTML = `
                        <span style="flex-grow: 1; display: flex; align-items: center;">${note.title}</span>
                        <button class="delete-note-button" title="Delete note">×</button>
                    `;

                    noteDiv.onclick = (e) => {
                        if (!e.target.classList.contains('delete-note-button')) {
                            container.parentElement.parentElement.remove();
                            showNoteContent(note, url, index);
                        }
                    };

                    noteDiv.querySelector('.delete-note-button').onclick = (e) => {
                        e.stopPropagation();
                        if (confirm('Are you sure you want to delete this note?')) {
                            deleteNote(url, index);
                            noteDiv.remove();
                            if (notes[url].length === 1) {
                                urlDiv.remove();
                            }
                        }
                    };

                    urlDiv.appendChild(noteDiv);
                });

                container.appendChild(urlDiv);
            }
        }

        createModal(container);
    }

    function setupShortcutListener() {
        document.removeEventListener('keydown', shortcutHandler);
        document.addEventListener('keydown', shortcutHandler);
    }

    function shortcutHandler(e) {
        if (matchShortcut(e, options.shortcuts.newNote)) {
            e.preventDefault();
            showNoteForm();
        }
        if (matchShortcut(e, options.shortcuts.currentPageNotes)) {
            e.preventDefault();
            showCurrentPageNotes();
        }
        if (matchShortcut(e, options.shortcuts.allNotes)) {
            e.preventDefault();
            showAllNotes();
        }
        if (matchShortcut(e, options.shortcuts.showOptions)) {
            e.preventDefault();
            showOptionsMenu();
        }
    }

    function matchShortcut(e, shortcut) {
        return e.ctrlKey === shortcut.ctrlKey &&
               e.shiftKey === shortcut.shiftKey &&
               e.altKey === shortcut.altKey &&
               e.key.toLowerCase() === shortcut.key.toLowerCase();
    }

    displayPinnedNotes();
    setupShortcutListener();

    // Register menu commands
    GM_registerMenuCommand('New Note', () => showNoteForm());
    GM_registerMenuCommand('View Notes (Current Page)', showCurrentPageNotes);
    GM_registerMenuCommand('View All Notes', showAllNotes);
    GM_registerMenuCommand('Options', showOptionsMenu);

})();