Newer
Older
TriliumScripts / highlight_todotxt.js
0xRoM on 21 Mar 5 KB initial commit
const TPL = `<div style="padding: 10px; border-top: 1px solid var(--main-border-color); contain: none;">
    <strong>Todo Highlighting Applied</strong>
</div>`;

class MoodlogHighlighterWidget extends api.NoteContextAwareWidget {
    static get parentWidget() { 
        //console.log("MDLG: Getting parent widget: center-pane");
        return 'center-pane'; 
    }

    get position() { 
        //console.log("MDLG: Setting position: 100");
        return 100; 
    }

    isEnabled() {
        const enabled = super.isEnabled() 
            && this.note.type === 'text' 
            && this.note.hasLabel('todotxt');
        //console.log(`MDLG: isEnabled: ${enabled}`);
        return enabled;
    }

    doRender() {
        //console.log("MDLG: Rendering widget...");
        this.$widget = $(TPL);
        //console.log("MDLG: Widget rendered:", this.$widget);
        return this.$widget;
    }

    async refreshWithNote(note) {
        //console.log("MDLG: Refreshing with note:", note);
        const { content } = await note.getNoteComplement();
        //console.log("MDLG: Note content fetched:", content);

        // Check if content is fetched properly
        if (!content) {
            //console.log("MDLG: No content fetched. Exiting refresh.");
            return;
        }

        const highlightedContent = this.applyHighlighting(content);
        //console.log("MDLG: Highlighted content:", highlightedContent);

        if (highlightedContent !== content) {
            //console.log("MDLG: Updating note content with highlighted version");

            // Use the setNoteContent function to update the note content
            try {
                await setNoteContent(note.noteId, highlightedContent);
                //console.log("MDLG: Content updated with highlighted version");
            } catch (error) {
                //console.error("MDLG: Error updating content:", error);
            }
        } else {
            //console.log("MDLG: No changes to content, skipping update.");
        }
    }


applyHighlighting(content) {
    // Replace <br> with \n, then remove all other HTML tags
    const plainTextContent = content.replace(/<br\s*\/?\>/gi, '\n').replace(/<[^>]*>/g, ' ');

    // Regular expressions for Todo.txt syntax
    const datePattern = /\b(0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([1-2]\d{3})\b/g;
    const completedTaskPattern = /^\s*x\s+.*/g;
    const priorityPattern = /^(\s*\([A-E]\))\s+/;
    const projectPattern = /(?:\s|^)(\+\S+)/g;
    const contextPattern = /(?:\s|^)(@\S+)/g;
    const waitPattern = /\bWAIT\b/gi;
    const customAttributePattern = /(\s+[^\s:]+:[^\s:]+)+\s*$/g;
    
    function escapeHtml(str) {
        return str.replace(/[&<>"']/g, char => ({
            '&': '&amp;', '<': '&lt;', '>': '&gt;',
            '"': '&quot;', "'": '&#039;'
        })[char]);
    }

    let highlightedLines = plainTextContent.split("\n").map(line => {
        // If line starts with 'x', make the whole line grey
        if (/^\s*x\s+/.test(line)) {
            return `<span style="color: grey;">${line}</span>`;
        }
        
        // Highlight priority with different colours based on level
        line = line.replace(priorityPattern, (match, priority) => {
            const level = priority.match(/[A-E]/)?.[0];
            const colors = {
                'A': 'rgb(255, 0, 0)',       // Red
                'B': 'rgb(255, 102, 0)',     // Orange
                'C': 'rgb(255, 204, 0)',     // Yellow
                'D': 'rgb(0, 153, 255)',     // Blue
                'E': 'rgb(102, 204, 102)'    // Green
            };
            const color = colors[level] || 'black';
            return `<span style="color: ${color}; font-weight: bold;">${escapeHtml(priority)}</span> `;
        });
        
        // Highlight date
        line = line.replace(datePattern, (match) => {
            return `<span style="color: rgb(188, 55, 237);">${escapeHtml(match)}</span>`;
        });

        // Highlight project tags
        line = line.replace(projectPattern, (_, tag) => `<span style="color: rgb(224, 218, 36);"> ${escapeHtml(tag)}</span>`);
        
        // Highlight context tags
        line = line.replace(contextPattern, (_, tag) => `<span style="color: rgb(255, 165, 10);"> ${escapeHtml(tag)}</span>`);
        
        // Highlight WAIT command
        line = line.replace(waitPattern, `<span style="color: rgb(255, 69, 0);">WAIT</span>`);
        
        // Highlight custom attributes
        line = line.replace(customAttributePattern, `<span style="color: rgb(100, 149, 237);">$1</span>`);
        
        // Remove leading space (if any) from the beginning of the line, without affecting spaces before + or @
        line = line.replace(/^\s+/, '');
        
        return line.trim();
    });

    return highlightedLines.join("<br>");
}





    async entitiesReloadedEvent({ loadResults }) {
        //console.log("MDLG: Entities reloaded event triggered");
        if (loadResults.isNoteContentReloaded(this.noteId)) {
            //console.log("MDLG: Note content reloaded, refreshing widget...");
            //this.refresh();
        } else {
            //console.log("MDLG: No content reload detected.");
        }
    }
    

}

console.log("TodoHighlight: Starting todotxt Highlighter Widget");
module.exports = MoodlogHighlighterWidget;

function setNoteContent(noteId, content) {
    /*const noteElement = document.querySelector(`note-detail-readonly-text-content`);

    if (noteElement) {
        noteElement.innerHTML = content; // Update the displayed content
        console.log(`MDLG: Updated note ${noteId} in the DOM.`);
    } else {
        console.warn(`MDLG: Note element with data-note-id='${noteId}' not found.`);
    }*/
    return api.runOnBackend((id, data) => api.getNote(id).setContent(data), [noteId, content]);
}