// ==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 ' ' + ' '.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);
})();