const TPL = `<div style="padding: 10px; border-top: 1px solid var(--main-border-color); contain: none;"> <strong>Moodlog 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('moodlog'); //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) { //console.log("MDLG: Applying highlighting to content..."); // Replace <br> with \n, then remove all other HTML tags const plainTextContent = content.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]*>/g, ' '); //console.log("MDLG: Stripped content:", plainTextContent); // Combined regex for date & time const dateTimePattern = /([1-2]\d{3}[-/.](0?[1-9]|1[0-2])[-/.](0[1-9]|[12]\d|3[01]))\s+(\d{1,2}:\d{2})/g; const projectPattern = /\[\S+?\]/g; const contextPattern = /(\s|^)(@\S+)/g; const hashtagPattern = /(\s|^)(#\S+)/g; const positivePattern = /(\s|^)(\+\S+)/g; const negativePattern = /(\s|^)(-\S+)/g; // Escape HTML characters to prevent issues with replacements function escapeHtml(str) { return str.replace(/[&<>"']/g, function (char) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]; }); } // Helper function to map the number to a colour based on the gradient function getProjectColour(number) { const minColor = { r: 107, g: 109, b: 229 }; // #6b6de5 const maxColor = { r: 70, g: 226, b: 96 }; // #46e260 const ratio = number / 9; // since we are working with 0-9 // Interpolate the RGB values between minColor and maxColor const r = Math.round(minColor.r + ratio * (maxColor.r - minColor.r)); const g = Math.round(minColor.g + ratio * (maxColor.g - minColor.g)); const b = Math.round(minColor.b + ratio * (maxColor.b - minColor.b)); return `rgb(${r}, ${g}, ${b})`; } // Process each line of the plain text content let highlightedLines = plainTextContent.split("\n").map(line => { //console.log("MDLG: Processing line:", line); // Apply combined date & time highlight line = line.replace(dateTimePattern, `<span style="color: rgb(188, 55, 237);">$1</span> <span style="color: rgb(188, 55, 237);">$4</span>`); // Apply other highlights line = line.replace(projectPattern, (match) => { // Extract the number inside square brackets const number = parseInt(match.match(/\d+/)[0], 10); const colour = getProjectColour(number); return `<span style="color: ${colour}; font-weight: bold;">${escapeHtml(match)}</span>`; }); // Apply other highlights //line = line.replace(projectPattern, `<span style="color: rgb(224, 218, 36); font-weight: bold;">$&</span>`); line = line.replace(contextPattern, (_, space, tag) => `${space}<span style="color: rgb(255 165 10);">${escapeHtml(tag)}</span>`); line = line.replace(hashtagPattern, (_, space, tag) => `${space}<span style="color: rgb(41, 229, 242);">${escapeHtml(tag)}</span>`); line = line.replace(positivePattern, (_, space, tag) => `${space}<span style="color: rgb(44, 227, 34);">${escapeHtml(tag)}</span>`); line = line.replace(negativePattern, (_, space, tag) => `${space}<span style="color: rgb(230, 59, 44);">${escapeHtml(tag)}</span>`); return line.trim(); }); // Join lines with proper <br> handling let finalContent = highlightedLines.join("<br>"); //console.log("MDLG: Highlighted content:", finalContent); return finalContent; } 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("MDLG: Starting Moodlog 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]); }