Claude Edit Comment v1.0.0

← Back to User Scripts

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

})();