Professional Website Notes Manager

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

当前为 2025-02-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Professional Website Notes Manager
// @namespace    http://tampermonkey.net/
// @version      0.8
// @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
// @grant        GM_listValues
// @grant        GM_deleteValue
// ==/UserScript==

(function() {
    'use strict';

    let scriptVersion = '0.8'

    const defaultOptions = {
        version: scriptVersion,
        darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches,
        addTimestampToTitle: false,
        showUrlLinksInNotesList: true,
        autoBackup: 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;
        try {
            currentOptions = GM_getValue('options', defaultOptions);
        } catch (error) {
            console.error('Error loading options, resetting to defaults:', error);
            return defaultOptions;
        }

        // If options is not an object for some reason
        if (!currentOptions || typeof currentOptions !== 'object') {
            console.warn('Invalid options found, resetting to defaults');
            return 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)
            if (!currentOptions.shortcuts || typeof currentOptions.shortcuts !== 'object') {
                currentOptions.shortcuts = defaultOptions.shortcuts;
            } else {
                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%;
            max-height: 90vh;
            overflow-y: auto;
            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};
        }

        .notes-overlay .notes-editor-toolbar {
            display: flex;
            gap: 8px;
            margin: 8px 0;
            padding: 8px;
            background: ${isDarkMode ? '#2a3441' : '#f3f4f6'};
            border-radius: 6px;
        }

        .notes-overlay .notes-tag {
            display: inline-block;
            padding: 4px 8px;
            margin: 0 4px 4px 0;
            border-radius: 4px;
            background: ${isDarkMode ? '#4b5563' : '#e5e7eb'};
            color: ${isDarkMode ? '#f3f4f6' : '#374151'};
            font-size: 12px;
        }
    `;


    const mobileStyles = `
        @media (max-width: 768px) {
            .notes-overlay .notes-modal {
                width: 95%;
                padding: 16px;
                max-height: 95vh;
            }

            .notes-overlay .notes-button {
                padding: 10px 16px;
                margin: 3px;
                font-size: 14px;
            }

            .notes-overlay .close-button {
                top: 8px;
                right: 8px;
            }

            .notes-overlay .button-group {
                display: flex;
                flex-direction: column;
            }

            .notes-overlay .notes-list-item {
                padding: 12px;
            }
        }
    `;

    const styleSheet = document.createElement("style");
    styleSheet.innerText = styles + mobileStyles;
    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>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="autoBackupToggle" ${options.autoBackup ? 'checked' : ''}>
                    Enable automatic backups
                </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>
            <div style="margin-top: 20px; display: flex; gap: 10px;">
                <button id="saveOptions" class="notes-button">Save Options</button>
                <button id="exportNotesBtn" class="notes-button secondary">Export Notes</button>
                <button id="importNotesBtn" class="notes-button secondary">Import Notes</button>
            </div>
        `;

        createModal(container);

        addRestoreBackupButton();

        // Add event listeners
        document.getElementById('saveOptions').onclick = saveOptions;
        document.getElementById('exportNotesBtn').onclick = exportNotes;
        document.getElementById('importNotesBtn').onclick = importNotes;
    }

    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) {
        if (!str || typeof str !== 'string') {
            console.warn('Invalid shortcut string:', str);
            // Return default values if string is invalid
            return {
                ctrlKey: true,
                shiftKey: true,
                altKey: false,
                key: 'S'
            };
        }

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

    // Replace the saveOptions function with this corrected version
    function saveOptions() {
        try {
            options = {
                version: scriptVersion,
                darkMode: document.getElementById('darkModeToggle').checked,
                addTimestampToTitle: document.getElementById('timestampToggle').checked,
                showUrlLinksInNotesList: document.getElementById('showUrlLinksToggle').checked,
                autoBackup: document.getElementById('autoBackupToggle').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 successfully. Some changes may require reloading the page.');
        } catch (error) {
            console.error('Error saving options:', error);
            alert('Failed to save options. Please try again.');
        }
    }

    function exportNotes() {
        try {
            const notes = getAllNotes();
            const dateInfo = getFormattedBackupDate();
            const blob = new Blob([JSON.stringify(notes, null, 2)], {type: 'application/json'});
            const url = URL.createObjectURL(blob);

            const a = document.createElement('a');
            a.href = url;
            a.download = `website-notes-backup-${dateInfo.formatted}.json`;
            a.click();

            URL.revokeObjectURL(url);
        } catch (error) {
            console.error('Error exporting notes:', error);
            alert('Failed to export notes. Please try again.');
        }
    }

    function importNotes() {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = '.json';

        input.onchange = (e) => {
            const file = e.target.files[0];
            if (!file) return;

            const reader = new FileReader();

            reader.onload = (event) => {
                try {
                    const importedNotes = JSON.parse(event.target.result);

                    // Create custom modal for import options
                    const overlay = document.createElement('div');
                    overlay.className = 'notes-overlay';

                    const modal = document.createElement('div');
                    modal.className = 'notes-modal';
                    modal.style.maxWidth = '500px';

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

                    modal.innerHTML = `
                        <h3 class="modal-title">Import Notes</h3>
                        <p>Choose how to import the notes:</p>

                        <div class="notes-list-item" style="cursor: pointer; margin-bottom: 12px;">
                            <div>
                                <strong>Merge</strong>
                                <p style="margin: 5px 0; font-size: 14px; color: ${isDarkMode ? '#9ca3af' : '#6b7280'}">
                                    Add imported notes to your existing notes. This will keep all your current notes and may create duplicates.
                                </p>
                            </div>
                        </div>

                        <div class="notes-list-item" style="cursor: pointer;">
                            <div>
                                <strong>Replace</strong>
                                <p style="margin: 5px 0; font-size: 14px; color: ${isDarkMode ? '#9ca3af' : '#6b7280'}">
                                    Replace all your current notes with the imported ones. This will delete all your existing notes.
                                </p>
                            </div>
                        </div>

                        <div style="display: flex; justify-content: space-between; margin-top: 20px;">
                            <button id="mergeBtn" class="notes-button">Merge</button>
                            <button id="replaceBtn" class="notes-button delete">Replace</button>
                            <button id="cancelBtn" class="notes-button secondary">Cancel</button>
                        </div>
                    `;

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

                    // Add event listeners
                    document.getElementById('mergeBtn').onclick = () => {
                        mergeNotes(importedNotes);
                        overlay.remove();
                    };

                    document.getElementById('replaceBtn').onclick = () => {
                        if (confirm('This will permanently replace all your existing notes. Are you sure?')) {
                            GM_setValue('website-notes', importedNotes);
                            alert('Notes replaced successfully!');
                            overlay.remove();
                        }
                    };

                    document.getElementById('cancelBtn').onclick = () => {
                        overlay.remove();
                    };

                } catch (error) {
                    console.error('Error parsing imported notes:', error);
                    alert('Error importing notes: Invalid format');
                }
            };

            reader.readAsText(file);
        };

        input.click();
    }

    function mergeNotes(importedNotes) {
        try {
            // Get existing notes
            const existingNotes = getAllNotes();

            // Count imported notes for notification
            let importedCount = 0;

            // Merge notes by URL
            for (const url in importedNotes) {
                if (existingNotes[url]) {
                    // If URL exists, append notes to existing array
                    existingNotes[url] = existingNotes[url].concat(importedNotes[url]);
                    importedCount += importedNotes[url].length;
                } else {
                    // If URL is new, add all notes
                    existingNotes[url] = importedNotes[url];
                    importedCount += importedNotes[url].length;
                }
            }

            // Save merged notes back to storage
            GM_setValue('website-notes', existingNotes);

            // Perform auto-backup if enabled
            if (options.autoBackup) {
                performAutoBackup();
            }

            alert(`Notes merged successfully! ${importedCount} notes were imported.`);
        } catch (error) {
            console.error('Error merging notes:', error);
            alert('Error merging notes. Please try again.');
        }
    }

    function addRestoreBackupButton() {
        // Create a restore backup button
        const restoreBackupBtn = document.createElement('button');
        restoreBackupBtn.id = 'restoreBackupBtn';
        restoreBackupBtn.className = 'notes-button secondary';
        restoreBackupBtn.textContent = 'Restore Backup';

        // Add it to the export/import button group
        const buttonGroup = document.querySelector('[id="saveOptions"]').parentNode;
        buttonGroup.appendChild(restoreBackupBtn);

        // Add event listener
        document.getElementById('restoreBackupBtn').onclick = showBackupsList;
    }

    function showBackupsList() {
        // Create modal for backup list
        const overlay = document.createElement('div');
        overlay.className = 'notes-overlay';

        const modal = document.createElement('div');
        modal.className = 'notes-modal';
        modal.style.maxWidth = '500px';

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

        let backupKeys = [];
        try {
            backupKeys = GM_listValues().filter(key => key.startsWith('notes-backup-')).sort().reverse();
        } catch (error) {
            console.warn('Could not retrieve list of backups:', error);
        }

        if (backupKeys.length === 0) {
            modal.innerHTML = `
            <h3 class="modal-title">Restore Backup</h3>
            <p>No backups found. Automatic backups are ${options.autoBackup ? 'enabled' : 'disabled'} in your settings.</p>
            <button id="closeBackupsList" class="notes-button">Close</button>
            `;
        } else {
            modal.innerHTML = `
            <h3 class="modal-title">Available Backups</h3>
            <p>Select a backup to restore:</p>
            <div id="backupsList" style="max-height: 300px; overflow-y: auto;"></div>
            <button id="closeBackupsList" class="notes-button secondary" style="margin-top: 16px;">Cancel</button>
            `;

            const backupsList = modal.querySelector('#backupsList');

            backupKeys.forEach(key => {
                // Extract the timestamp from the key
                const timestampStr = key.replace('notes-backup-', '');
                let timestamp;
                let readableDate = "Unknown date";

                // Handle both timestamp formats
                if (/^\d+$/.test(timestampStr)) {
                    // It's a numeric timestamp
                    timestamp = parseInt(timestampStr, 10);
                } else if (timestampStr.includes('T')) {
                    // It's an ISO date format
                    try {
                        timestamp = new Date(timestampStr.replace(/\-/g, ':')).getTime();
                    } catch (e) {
                        console.error('Error parsing ISO date format:', e);
                    }
                }

                // Format date in a more user-friendly way
                if (!isNaN(timestamp) && timestamp > 0) {
                    const date = new Date(timestamp);

                    // Format: "Feb 25, 2025 - 3:45 PM" (with day and time)
                    const options = {
                        year: 'numeric',
                        month: 'short',
                        day: 'numeric',
                        hour: 'numeric',
                        minute: '2-digit',
                        hour12: true
                    };
                    readableDate = date.toLocaleDateString(undefined, options);

                    // Add relative time indication like "Today", "Yesterday", etc.
                    const today = new Date();
                    const yesterday = new Date(today);
                    yesterday.setDate(yesterday.getDate() - 1);

                    if (date.toDateString() === today.toDateString()) {
                        readableDate = `Today, ${date.toLocaleTimeString(undefined, {hour: 'numeric', minute: '2-digit', hour12: true})}`;
                    } else if (date.toDateString() === yesterday.toDateString()) {
                        readableDate = `Yesterday, ${date.toLocaleTimeString(undefined, {hour: 'numeric', minute: '2-digit', hour12: true})}`;
                    }
                }

                const backupItem = document.createElement('div');
                backupItem.className = 'notes-list-item';
                backupItem.innerHTML = `<span>${readableDate}</span>`;
                backupItem.onclick = () => confirmAndRestoreBackup(key);

                backupsList.appendChild(backupItem);
            });
        }

        modal.appendChild(closeButton);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        document.getElementById('closeBackupsList')?.addEventListener('click', () => overlay.remove());
    }

    function confirmAndRestoreBackup(backupKey) {
        if (confirm('Are you sure you want to restore this backup? This will replace all your current notes.')) {
            try {
                const backupData = GM_getValue(backupKey);
                if (backupData) {
                    GM_setValue('website-notes', backupData);
                    alert('Backup restored successfully!');
                    location.reload(); // Reload the page to refresh notes display
                } else {
                    alert('Error: Backup data is empty or corrupted.');
                }
            } catch (error) {
                console.error('Error restoring backup:', error);
                alert('Failed to restore backup. Please try again.');
            }
        }
    }

    // Add search functionality
    function addSearchButton() {
        // Add a search button to the top of the all notes view
        const searchButton = document.createElement('button');
        searchButton.className = 'notes-button';
        searchButton.textContent = '🔍 Search Notes';
        searchButton.style.marginBottom = '16px';
        searchButton.onclick = showSearchModal;

        // Find the appropriate container - the div after the modal title
        const titleElement = document.querySelector('.notes-modal .modal-title');
        if (titleElement && titleElement.textContent === 'All Notes') {
            titleElement.parentNode.insertBefore(searchButton, titleElement.nextSibling);
        }
    }

    function showSearchModal() {
        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.innerHTML = `
            <h3 class="modal-title">Search Notes</h3>
            <input type="text" id="searchInput" class="notes-input" placeholder="Search by title, content, tags, or URL...">
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="searchTitle" checked>
                    Search in titles
                </label>
            </div>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="searchContent" checked>
                    Search in note content
                </label>
            </div>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="searchTags" checked>
                    Search in tags
                </label>
            </div>
            <div class="notes-options-label">
                <label>
                    <input type="checkbox" class="notes-options-checkbox" id="searchUrls">
                    Search in URLs
                </label>
            </div>
            <div id="searchResults" style="margin-top: 16px; max-height: 400px; overflow-y: auto;"></div>
            <button id="closeSearchModal" class="notes-button secondary" style="margin-top: 16px;">Close</button>
        `;

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

        // Set up event listeners
        const searchInput = document.getElementById('searchInput');
        searchInput.focus();

        searchInput.addEventListener('input', performSearch);
        document.getElementById('searchTitle').addEventListener('change', performSearch);
        document.getElementById('searchContent').addEventListener('change', performSearch);
        document.getElementById('searchTags').addEventListener('change', performSearch);
        document.getElementById('searchUrls').addEventListener('change', performSearch);
        document.getElementById('closeSearchModal').addEventListener('click', () => overlay.remove());

        // Perform search when input changes
        function performSearch() {
            const query = searchInput.value.toLowerCase().trim();
            const searchTitle = document.getElementById('searchTitle').checked;
            const searchContent = document.getElementById('searchContent').checked;
            const searchTags = document.getElementById('searchTags').checked;
            const searchUrls = document.getElementById('searchUrls').checked;

            const searchResults = document.getElementById('searchResults');
            searchResults.innerHTML = '';

            if (!query) {
                searchResults.innerHTML = '<p style="color: #6b7280;">Enter a search term to find notes</p>';
                return;
            }

            const notes = getAllNotes();
            let resultCount = 0;

            // Function to highlight matching text
            function highlightMatch(text, query) {
                if (!text) return '';
                const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
                return text.replace(regex, '<mark style="background-color: #fde68a; color: #1f2937;">$1</mark>');
            }

            // Search through all notes
            for (const url in notes) {
                if (searchUrls && url.toLowerCase().includes(query)) {
                    // The URL itself matches
                    const urlDiv = document.createElement('div');
                    urlDiv.innerHTML = `<div class="url-text">${highlightMatch(url, query)}</div>`;

                    // Add all notes under this URL
                    notes[url].forEach((note, index) => {
                        addNoteResult(urlDiv, note, url, index);
                    });

                    searchResults.appendChild(urlDiv);
                    resultCount += notes[url].length;
                    continue;
                }

                // Check if any notes match the search criteria
                const matchingNotes = notes[url].filter(note => {
                    if (searchTitle && note.title.toLowerCase().includes(query)) return true;
                    if (searchContent && note.content.toLowerCase().includes(query)) return true;
                    if (searchTags && note.tags && note.tags.some(tag => tag.toLowerCase().includes(query))) return true;
                    return false;
                });

                if (matchingNotes.length > 0) {
                    const urlDiv = document.createElement('div');
                    urlDiv.innerHTML = `<div class="url-text">${url}</div>`;

                    matchingNotes.forEach(note => {
                        const index = notes[url].indexOf(note);
                        addNoteResult(urlDiv, note, url, index, query);
                    });

                    searchResults.appendChild(urlDiv);
                    resultCount += matchingNotes.length;
                }
            }

            if (resultCount === 0) {
                searchResults.innerHTML = '<p style="color: #6b7280;">No matching notes found</p>';
            } else {
                searchResults.insertAdjacentHTML('afterbegin', `<p style="color: #6b7280;">${resultCount} note${resultCount !== 1 ? 's' : ''} found</p>`);
            }

            // Helper function to add a note result to the results div
            function addNoteResult(container, note, url, index, query) {
                const noteDiv = document.createElement('div');
                noteDiv.className = 'notes-list-item';

                // Apply note color if available
                if (note.color) {
                    noteDiv.style.borderLeft = `4px solid ${note.color}`;
                    noteDiv.style.paddingLeft = '12px';
                }

                // Create content with highlighted matches
                let titleHtml = note.title;
                let contentPreview = '';

                if (query) {
                    // Highlight matches
                    if (searchTitle) {
                        titleHtml = highlightMatch(note.title, query);
                    }

                    if (searchContent && note.content.toLowerCase().includes(query)) {
                        // Find the context around the match
                        const matchIndex = note.content.toLowerCase().indexOf(query);
                        const startIndex = Math.max(0, matchIndex - 50);
                        const endIndex = Math.min(note.content.length, matchIndex + query.length + 50);

                        // Add ellipsis if we're not starting from the beginning
                        let preview = (startIndex > 0 ? '...' : '') +
                                     note.content.substring(startIndex, endIndex) +
                                     (endIndex < note.content.length ? '...' : '');

                        contentPreview = `<div style="margin-top: 4px; font-size: 14px; color: ${isDarkMode ? '#9ca3af' : '#6b7280'};">
                                         ${highlightMatch(preview, query)}
                                         </div>`;
                    }
                }

                // Add tags if available with highlighting
                let tagsHTML = '';
                if (note.tags && note.tags.length > 0) {
                    tagsHTML = '<div style="margin-top: 4px;">';
                    note.tags.forEach(tag => {
                        if (searchTags && query && tag.toLowerCase().includes(query)) {
                            tagsHTML += `<span class="notes-tag">${highlightMatch(tag, query)}</span>`;
                        } else {
                            tagsHTML += `<span class="notes-tag">${tag}</span>`;
                        }
                    });
                    tagsHTML += '</div>';
                }

                noteDiv.innerHTML = `
                    <div style="flex-grow: 1;">
                        <div style="font-weight: 500;">${titleHtml}</div>
                        ${contentPreview}
                        ${tagsHTML}
                    </div>
                `;

                noteDiv.onclick = () => {
                    document.querySelector('.notes-overlay').remove();
                    showNoteContent(note, url, index);
                };

                container.appendChild(noteDiv);
            }
        }
    }


    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, tags = [], color = null) {
        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,
            tags,
            color
        });

        GM_setValue('website-notes', notes);

        // Perform auto-backup if enabled
        if (options.autoBackup) {
            performAutoBackup();
        }
    }

    function performAutoBackup() {
        try {
            const notes = getAllNotes();
            const dateInfo = getFormattedBackupDate();
            // Use consistent format with numeric timestamp
            const backupKey = `notes-backup-${dateInfo.timestamp}`;

            // Create the new backup
            GM_setValue(backupKey, notes);
            console.log(`Auto-backup created successfully: ${dateInfo.formatted}`);

            // Now try to manage old backups
            try {
                // Try to get all backup keys
                const allBackupKeys = GM_listValues().filter(key => key.startsWith('notes-backup-')).sort();

                // Keep only the last 5 backups
                if (allBackupKeys.length > 5) {
                    // Delete oldest backups, keeping the 5 most recent
                    for (let i = 0; i < allBackupKeys.length - 5; i++) {
                        try {
                            GM_deleteValue(allBackupKeys[i]);
                            console.log(`Deleted old backup: ${allBackupKeys[i]}`);
                        } catch (deleteError) {
                            alert(`Could not delete backup ${allBackupKeys[i]}:`, deleteError);
                        }
                    }
                }
            } catch (listError) {
                console.warn('Could not retrieve list of backups to manage old backups:', listError);
                alert('Could not retrieve list of backups to manage old backups:', listError);

                // Alternative approach: Store the list of backup keys ourselves
                let storedBackupKeys = GM_getValue('backup-key-list', []);

                // Add the new backup key to our list
                storedBackupKeys.push(backupKey);

                // Only keep the most recent 5 backups
                if (storedBackupKeys.length > 5) {
                    // Get keys to delete (all except the 5 most recent)
                    const keysToDelete = storedBackupKeys.slice(0, storedBackupKeys.length - 5);

                    // Delete old backups
                    keysToDelete.forEach(keyToDelete => {
                        try {
                            GM_deleteValue(keyToDelete);
                            console.log(`Deleted old backup (using fallback method): ${keyToDelete}`);
                        } catch (deleteError) {
                            console.warn(`Could not delete backup ${keyToDelete}:`, deleteError);
                        }
                    });

                    // Update our stored list to contain only the 5 most recent keys
                    storedBackupKeys = storedBackupKeys.slice(storedBackupKeys.length - 5);
                }

                // Save the updated list of backup keys
                GM_setValue('backup-key-list', storedBackupKeys);
            }
        } catch (error) {
            console.error('Error during auto-backup:', error);
        }
    }

    function getFormattedBackupDate() {
        const now = new Date();

        // Format: YYYY-MM-DD_HH-MM-SS (e.g., 2025-02-25_14-30-45)
        const year = now.getFullYear();
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const day = String(now.getDate()).padStart(2, '0');
        const hours = String(now.getHours()).padStart(2, '0');
        const minutes = String(now.getMinutes()).padStart(2, '0');
        const seconds = String(now.getSeconds()).padStart(2, '0');

        const dateString = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;

        return {
            timestamp: now.getTime(),  // Use numeric timestamp
            formatted: dateString
        };
    }

    function updateNote(oldUrl, index, title, newUrl, content, pinned, tags = [], color = null) {
        const notes = getAllNotes();
        const existingNote = notes[oldUrl][index];

        // Delete the old note
        deleteNote(oldUrl, index);

        // Save with updated values but keep the original timestamp
        saveNote(
            title,
            newUrl,
            content,
            existingNote.timestamp,
            pinned,
            tags,
            color
        );
    }

    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) {
        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/*)';

        // Add tags input
        const tagsInput = document.createElement('input');
        tagsInput.className = 'notes-input';
        tagsInput.placeholder = 'Tags (comma separated)';
        tagsInput.value = editMode && existingNote.tags ? existingNote.tags.join(', ') : '';

        const tagsHelp = document.createElement('div');
        tagsHelp.style.fontSize = '12px';
        tagsHelp.style.color = isDarkMode ? '#9ca3af' : '#6b7280';
        tagsHelp.style.marginTop = '-8px';
        tagsHelp.style.marginBottom = '8px';
        tagsHelp.innerHTML = 'Add tags to organize notes (e.g., work, personal, important)';

        // Add color picker
        const colorPicker = createColorPicker(editMode && existingNote.color ? existingNote.color : '#3b82f6');
        const colorPickerLabel = document.createElement('div');
        colorPickerLabel.style.fontSize = '14px';
        colorPickerLabel.style.marginBottom = '8px';
        colorPickerLabel.innerHTML = 'Note Color:';

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

        // Add formatting toolbar
        const toolbar = enhanceTextEditor(contentArea);

        const buttonGroup = document.createElement('div');
        buttonGroup.className = 'button-group';
        buttonGroup.style.display = 'flex';
        buttonGroup.style.justifyContent = 'space-between';
        buttonGroup.style.marginTop = '16px';

        const saveButton = document.createElement('button');
        saveButton.className = 'notes-button';
        saveButton.textContent = editMode ? 'Update Note' : 'Save Note';
        saveButton.onclick = () => {
            if (titleInput.value && contentArea.value) {
                const tags = tagsInput.value.split(',').map(tag => tag.trim()).filter(tag => tag);
                const color = colorPicker.dataset.selectedColor;

                if (editMode) {
                    updateNote(url, index, titleInput.value, urlInput.value, contentArea.value,
                               existingNote.pinned, tags, color);
                } else {
                    saveNote(titleInput.value, urlInput.value, contentArea.value, Date.now(), false, tags, color);
                }
                container.parentElement.parentElement.remove();
                showCurrentPageNotes();
            } else {
                alert('Title and content are required!');
            }
        };

        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(tagsInput);
        container.appendChild(tagsHelp);
        container.appendChild(colorPickerLabel);
        container.appendChild(colorPicker);
        container.appendChild(toolbar);
        container.appendChild(contentArea);
        container.appendChild(buttonGroup);

        createModal(container);
    }

    function createColorPicker(selectedColor = '#3b82f6') {
        const colorOptions = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899'];

        const container = document.createElement('div');
        container.style.display = 'flex';
        container.style.gap = '8px';
        container.style.margin = '8px 0';
        container.style.flexWrap = 'wrap';

        colorOptions.forEach(color => {
            const option = document.createElement('div');
            option.style.width = '24px';
            option.style.height = '24px';
            option.style.borderRadius = '50%';
            option.style.backgroundColor = color;
            option.style.cursor = 'pointer';
            option.style.border = color === selectedColor ? '2px solid white' : '2px solid transparent';
            option.style.boxShadow = '0 0 0 1px rgba(0,0,0,0.1)';

            option.onclick = () => {
                container.querySelectorAll('div').forEach(div => {
                    div.style.border = '2px solid transparent';
                });
                option.style.border = '2px solid white';
                container.dataset.selectedColor = color;
            };

            container.appendChild(option);
        });

        container.dataset.selectedColor = selectedColor;
        return container;
    }

    function applyNoteColor(noteElement, color) {
        if (!color) return;

        // Apply color as a left border
        noteElement.style.borderLeft = `4px solid ${color}`;
        // Add subtle background tint
        const colorOpacity = isDarkMode ? '0.1' : '0.05';
        noteElement.style.backgroundColor = `${color}${colorOpacity}`;
    }

    function enhanceTextEditor(textArea) {
        const toolbar = document.createElement('div');
        toolbar.className = 'notes-editor-toolbar';

        const addButton = (text, title, action) => {
            const btn = document.createElement('button');
            btn.textContent = text;
            btn.title = title;
            btn.className = 'notes-button secondary';
            btn.style.padding = '4px 8px';
            btn.style.fontSize = '12px';
            btn.onclick = (e) => {
                e.preventDefault();
                action(textArea);
                textArea.focus(); // Keep focus on the textarea after button click
            };
            return btn;
        };

        // Add formatting buttons with icons or text
        toolbar.appendChild(addButton('B', 'Bold (Ctrl+B)', ta => {
            // If text is selected, wrap it in bold marks
            // Otherwise, just insert the marks and place cursor between them
            insertAround(ta, '**', '**');
        }));

        toolbar.appendChild(addButton('I', 'Italic (Ctrl+I)', ta => {
            insertAround(ta, '_', '_');
        }));

        toolbar.appendChild(addButton('Link', 'Insert Link', ta => {
            const selection = ta.value.substring(ta.selectionStart, ta.selectionEnd);
            if (selection) {
                insertAround(ta, '[', '](https://)');
                // Position cursor after the opening bracket of the URL
                ta.selectionStart = ta.selectionEnd - 9;
                ta.selectionEnd = ta.selectionEnd - 1;
            } else {
                insertAtCursor(ta, '[Link text](https://)');
                // Select "Link text" for easy replacement
                const cursorPos = ta.value.lastIndexOf('[Link text]');
                ta.selectionStart = cursorPos + 1;
                ta.selectionEnd = cursorPos + 10;
            }
        }));

        toolbar.appendChild(addButton('List', 'Insert List', ta => {
            insertAtCursor(ta, '\n- Item 1\n- Item 2\n- Item 3\n');
        }));

        toolbar.appendChild(addButton('H1', 'Heading 1', ta => {
            const start = ta.selectionStart;
            const lineStart = ta.value.lastIndexOf('\n', start - 1) + 1;
            const selection = ta.value.substring(ta.selectionStart, ta.selectionEnd);

            // Check if the line already starts with # to avoid duplicating
            const currentLine = ta.value.substring(lineStart, start);
            if (currentLine.trim().startsWith('# ')) {
                return; // Already has heading format
            }

            if (selection) {
                // Selected text becomes heading
                ta.value = ta.value.substring(0, ta.selectionStart) +
                           '# ' + selection +
                           ta.value.substring(ta.selectionEnd);
                ta.selectionStart = ta.selectionStart + 2;
                ta.selectionEnd = ta.selectionStart + selection.length;
            } else {
                // Insert at current line start
                ta.value = ta.value.substring(0, lineStart) +
                           '# Heading' +
                           ta.value.substring(lineStart);
                ta.selectionStart = lineStart + 2;
                ta.selectionEnd = lineStart + 9;
            }
        }));

        toolbar.appendChild(addButton('H2', 'Heading 2', ta => {
            const start = ta.selectionStart;
            const lineStart = ta.value.lastIndexOf('\n', start - 1) + 1;
            const selection = ta.value.substring(ta.selectionStart, ta.selectionEnd);

            // Check if the line already starts with ## to avoid duplicating
            const currentLine = ta.value.substring(lineStart, start);
            if (currentLine.trim().startsWith('## ')) {
                return; // Already has heading format
            }

            if (selection) {
                // Selected text becomes heading
                ta.value = ta.value.substring(0, ta.selectionStart) +
                           '## ' + selection +
                           ta.value.substring(ta.selectionEnd);
                ta.selectionStart = ta.selectionStart + 3;
                ta.selectionEnd = ta.selectionStart + selection.length;
            } else {
                // Insert at current line start
                ta.value = ta.value.substring(0, lineStart) +
                           '## Subheading' +
                           ta.value.substring(lineStart);
                ta.selectionStart = lineStart + 3;
                ta.selectionEnd = lineStart + 13;
            }
        }));

        toolbar.appendChild(addButton('Quote', 'Blockquote', ta => {
            const start = ta.selectionStart;
            const lineStart = ta.value.lastIndexOf('\n', start - 1) + 1;
            const selection = ta.value.substring(ta.selectionStart, ta.selectionEnd);

            if (selection) {
                // Add quote prefix to all selected lines
                const lines = selection.split('\n');
                const quotedText = lines.map(line => `> ${line}`).join('\n');

                ta.value = ta.value.substring(0, ta.selectionStart) +
                           quotedText +
                           ta.value.substring(ta.selectionEnd);

                ta.selectionStart = ta.selectionStart;
                ta.selectionEnd = ta.selectionStart + quotedText.length;
            } else {
                // Insert at current line start
                ta.value = ta.value.substring(0, lineStart) +
                           '> ' + ta.value.substring(lineStart);
                ta.selectionStart = lineStart + 2;
                ta.selectionEnd = lineStart + 2;
            }
        }));

        // Add keyboard event listeners for common shortcuts
        textArea.addEventListener('keydown', (e) => {
            // Ctrl+B for bold
            if (e.ctrlKey && e.key === 'b') {
                e.preventDefault();
                insertAround(textArea, '**', '**');
            }
            // Ctrl+I for italic
            if (e.ctrlKey && e.key === 'i') {
                e.preventDefault();
                insertAround(textArea, '_', '_');
            }
            // Tab key handling for indentation
            if (e.key === 'Tab') {
                e.preventDefault();
                insertAtCursor(textArea, '    ');
            }
        });

        return toolbar;
    }

    function insertAround(textArea, before, after) {
        const start = textArea.selectionStart;
        const end = textArea.selectionEnd;
        const text = textArea.value;
        const selected = text.substring(start, end);

        textArea.value = text.substring(0, start) + before + selected + after + text.substring(end);
        textArea.focus();
        textArea.setSelectionRange(start + before.length, start + before.length + selected.length);
    }

    function insertAtCursor(textArea, text) {
        const start = textArea.selectionStart;
        textArea.value = textArea.value.substring(0, start) + text + textArea.value.substring(start);
        textArea.focus();
        textArea.setSelectionRange(start + text.length, start + text.length);
    }

    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>`;
            });

            // Process markdown formatting
            let formattedText = linkedText
                // Bold
                .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
                // Italic
                .replace(/_(.*?)_/g, '<em>$1</em>')
                // Headers
                .replace(/^# (.*?)$/gm, '<h1 style="font-size: 1.5em; margin-top: 0.8em; margin-bottom: 0.5em;">$1</h1>')
                .replace(/^## (.*?)$/gm, '<h2 style="font-size: 1.3em; margin-top: 0.7em; margin-bottom: 0.4em;">$1</h2>')
                // Lists
                .replace(/^- (.*?)$/gm, '• $1<br>')
                // Blockquotes
                .replace(/^> (.*?)$/gm, '<blockquote style="border-left: 3px solid #9ca3af; padding-left: 10px; margin-left: 5px; color: #6b7280;">$1</blockquote>');

            // Convert line breaks to <br> tags and maintain whitespace
            return formattedText.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);

        // Create note header with title and color
        const noteHeader = document.createElement('div');
        noteHeader.className = 'note-header';
        noteHeader.style.display = 'flex';
        noteHeader.style.alignItems = 'center';
        noteHeader.style.marginBottom = '16px';

        // Create color indicator
        const colorIndicator = document.createElement('div');
        colorIndicator.style.width = '16px';
        colorIndicator.style.height = '16px';
        colorIndicator.style.borderRadius = '50%';
        colorIndicator.style.marginRight = '8px';
        colorIndicator.style.backgroundColor = note.color || '#3b82f6';

        // Create the actual content container
        const contentContainer = document.createElement('div');
        contentContainer.className = 'note-content-container';
        contentContainer.style.padding = '16px';
        contentContainer.style.borderRadius = '8px';
        contentContainer.style.marginBottom = '16px';

        // Apply the note color
        if (note.color) {
            contentContainer.style.borderLeft = `4px solid ${note.color}`;
            contentContainer.style.backgroundColor = `${note.color}${isDarkMode ? '15' : '10'}`;
        } else {
            contentContainer.style.backgroundColor = isDarkMode ? '#2a3441' : '#f9fafb';
        }

        // Add tags display if the note has tags
        let tagsHTML = '';
        if (note.tags && note.tags.length > 0) {
            tagsHTML = '<div style="margin-top: 8px; margin-bottom: 8px;">';
            note.tags.forEach(tag => {
                tagsHTML += `<span class="notes-tag">${tag}</span>`;
            });
            tagsHTML += '</div>';
        }

        container.innerHTML = `
            <h3 class="modal-title">${note.title}</h3>
            <div class="url-text">${url}</div>
            <div class="timestamp">Created: ${formatDate(note.timestamp)}</div>
            ${tagsHTML}
        `;

        // Add content to the content container
        contentContainer.innerHTML = linkify(note.content);
        container.appendChild(contentContainer);

        // Add copy event listener to the content div
        contentContainer.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);
            // Get the updated notes data after toggling pin status
            const notes = getAllNotes();
            // Update the button text and class based on the updated pin status
            const isPinned = notes[url] && notes[url][index] ? notes[url][index].pinned : false;
            pinButton.textContent = isPinned ? 'Unpin' : 'Pin';
            pinButton.className = `notes-button ${isPinned ? '' : 'secondary'}`;
            // Update the pinned notes display
            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';

                        // Apply note color if available
                        if (note.color) {
                            noteDiv.style.borderLeft = `4px solid ${note.color}`;
                            noteDiv.style.paddingLeft = '12px';
                        }

                        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';

                    // Apply note color if available
                    if (note.color) {
                        noteDiv.style.borderLeft = `4px solid ${note.color}`;
                        noteDiv.style.paddingLeft = '12px';
                    }

                    // Add tags if available
                    let tagsHTML = '';
                    if (note.tags && note.tags.length > 0) {
                        tagsHTML = '<div style="margin-top: 4px;">';
                        note.tags.forEach(tag => {
                            tagsHTML += `<span class="notes-tag">${tag}</span>`;
                        });
                        tagsHTML += '</div>';
                    }

                    noteDiv.innerHTML = `
                        <div style="flex-grow: 1; display: flex; flex-direction: column;">
                            <span style="font-weight: 500;">${note.title}</span>
                            ${tagsHTML}
                        </div>
                        <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>';

        // Add a search button
        const searchButton = document.createElement('button');
        searchButton.className = 'notes-button';
        searchButton.textContent = '🔍 Search Notes';
        searchButton.style.marginBottom = '16px';
        searchButton.onclick = showSearchModal;
        container.appendChild(searchButton);

        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';

                    // Apply note color if available
                    if (note.color) {
                        noteDiv.style.borderLeft = `4px solid ${note.color}`;
                        noteDiv.style.paddingLeft = '12px';

                        // Add subtle background tint based on the note color
                        const colorOpacity = isDarkMode ? '0.1' : '0.05';
                        noteDiv.style.backgroundColor = `${note.color}${colorOpacity}`;
                    }

                    // Add tags if available
                    let tagsHTML = '';
                    if (note.tags && note.tags.length > 0) {
                        tagsHTML = '<div style="margin-top: 4px;">';
                        note.tags.forEach(tag => {
                            tagsHTML += `<span class="notes-tag">${tag}</span>`;
                        });
                        tagsHTML += '</div>';
                    }

                    // Add pin indicator if note is pinned
                    const pinnedIndicator = note.pinned ?
                          '<span title="Pinned" style="margin-right: 5px; color: #f59e0b;">📌</span>' : '';

                    noteDiv.innerHTML = `
                    <div style="flex-grow: 1; display: flex; flex-direction: column;">
                        <span style="font-weight: 500;">${pinnedIndicator}${note.title}</span>
                        ${tagsHTML}
                    </div>
                    <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);

})();