Claude Edit Comment v1.0.0
Description for Edit Comment v1.0.0.
Script Content
// ==UserScript==
// @name Todoist: Quick Edit Comment
// @namespace https://github.com/tjhleeds/user-scripts/
// @version 1.0
// @description Add quick edit button to Todoist comments
// @author tjhleeds
// @match https://app.todoist.com/app/task/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// SVG for the edit icon
const editIconSVG = ``;
function addEditButton(actionContainer) {
// Check if edit button already exists
if (actionContainer.querySelector('.quick-edit-btn')) {
return;
}
// Create the edit button
const editButton = document.createElement('button');
editButton.className = 'quick-edit-btn';
editButton.setAttribute('aria-label', 'Quick edit comment');
editButton.setAttribute('aria-disabled', 'false');
editButton.innerHTML = editIconSVG;
// Style the button to match existing buttons
editButton.style.cssText = `
background: none;
border: none;
cursor: pointer;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: inherit;
opacity: 0.7;
transition: opacity 0.2s ease;
`;
// Add hover effect
editButton.addEventListener('mouseenter', () => {
editButton.style.opacity = '1';
});
editButton.addEventListener('mouseleave', () => {
editButton.style.opacity = '0.7';
});
// Add click functionality
editButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
// Find the comment options button in the same container
const optionsButton = actionContainer.querySelector('button[aria-label="Comment options"]');
if (optionsButton) {
// Click the options button
optionsButton.click();
// Wait 50ms then click the first menu item (edit option)
setTimeout(() => {
const menuItems = document.querySelectorAll('div[aria-label="Comment options menu"] div[role="menuitem"]');
if (menuItems.length > 0) {
menuItems[0].click();
}
}, 50);
}
});
// Insert the edit button as the first child (leftmost position)
actionContainer.insertBefore(editButton, actionContainer.firstChild);
}
function processComments() {
const commentsPanel = document.getElementById('task-detail-comments-panel');
if (!commentsPanel) return;
// Find all comment action containers
const actionContainers = commentsPanel.querySelectorAll('.comments_meta.note_meta .JaQT53d');
actionContainers.forEach(container => {
// Only add button if this container has the expected buttons
const hasReactionButton = container.querySelector('button[aria-label="Add a reaction"]');
const hasOptionsButton = container.querySelector('button[aria-label="Comment options"]');
if (hasReactionButton && hasOptionsButton) {
addEditButton(container);
}
});
}
// Initial processing
processComments();
// Set up observer to watch for new comments or modal changes
const observer = new MutationObserver((mutations) => {
let shouldProcess = false;
mutations.forEach((mutation) => {
// Check if comments panel appeared or changed
if (mutation.type === 'childList') {
const addedNodes = Array.from(mutation.addedNodes);
const removedNodes = Array.from(mutation.removedNodes);
// Check if any added nodes contain comments or are the comments panel
const hasCommentsRelatedChanges = addedNodes.some(node => {
return node.nodeType === Node.ELEMENT_NODE && (
node.id === 'task-detail-comments-panel' ||
node.querySelector && node.querySelector('#task-detail-comments-panel') ||
node.classList && node.classList.contains('note_text') ||
node.querySelector && node.querySelector('.note_text')
);
});
if (hasCommentsRelatedChanges) {
shouldProcess = true;
}
}
});
if (shouldProcess) {
// Small delay to ensure DOM is fully updated
setTimeout(processComments, 100);
}
});
// Start observing
observer.observe(document.body, {
childList: true,
subtree: true
});
// Also check periodically in case we miss something
setInterval(processComments, 2000);
})();