From 2bf692cd410ed867dfdfc290faa253963867e245 Mon Sep 17 00:00:00 2001 From: alexa Date: Sun, 28 Apr 2024 22:41:32 +0200 Subject: [PATCH] md stuff --- ui/js/MarkdownProcessor.mjs | 146 +++++++++++++++++++---------- ui/templates/MarkdownTemplates.mjs | 29 +++++- 2 files changed, 120 insertions(+), 55 deletions(-) diff --git a/ui/js/MarkdownProcessor.mjs b/ui/js/MarkdownProcessor.mjs index 9cca371..99b7e50 100644 --- a/ui/js/MarkdownProcessor.mjs +++ b/ui/js/MarkdownProcessor.mjs @@ -18,45 +18,11 @@ export class MarkdownProcessor { level, text }); - } else if (line.startsWith('![')) { - const src = line.match(/\((.*?)\)/)[1]; - const alt = line.match(/\[(.*?)]/)[1]; - elements.push({ - type: 'image', - src, - alt - }); - } else if (line.startsWith('[')) { - const href = line.match(/\((.*?)\)/)[1]; - const text = line.match(/\[(.*?)]/)[1]; - elements.push({ - type: 'link', - href, - text - }); } else if (line.startsWith('```')) { elements.push({ type: 'code', text: line.replace(/```/, '') }); - } else if (line.match(/\*\*(.*?)\*\*/)) { // **bold** text - const text = line.match(/\*\*(.*?)\*\*/)[1]; - elements.push({ - type: 'bold', - text - }); - } else if (line.match(/\*(.*?)\*/)) { // *italic* text - const text = line.match(/\*(.*?)\*/)[1]; - elements.push({ - type: 'italic', - text - }); - } else if (line.match(/\*\*\*(.*?)\*\*\*/)) { // For ***bold and italic*** text - const text = line.match(/\*\*\*(.*?)\*\*\*/)[1]; - elements.push({ - type: 'boldItalic', - text - }); } else if (line.startsWith('>')) { // For quotes const level = line.match(/>+/)[0].length; const text = line.replace(/>+\s*/, ''); @@ -92,25 +58,108 @@ export class MarkdownProcessor { return elements; } + static parseText(text) { + const elements = []; + + // Create regular expressions for each formatting type + const formattingTypes = [ + {name: 'boldItalic', regex: /\*\*\*(.*?)\*\*\*/g}, + {name: 'bold', regex: /\*\*(.*?)\*\*/g}, + {name: 'italic', regex: /\*(.*?)\*/g}, + {name: 'link', regex: /\[(.*?)]\((.*?)\)/g}, + {name: 'image', regex: /!\[(.*?)]\((.*?)\)/g}, + ]; + + // Keep examining the text until all formatting has been extracted + while (text.length > 0) { + // Find the nearest formatting + let nearest = {index: text.length}; + let nearestType; + + for (let type of formattingTypes) { + const match = type.regex.exec(text); + if (match && match.index < nearest.index) { + nearest = match; + nearestType = type.name; + } + } + + // Add the text preceding the nearest match (if any) and the nearest match (if any) to the elements + if (nearest.index > 0) { + const element = { + type: 'text', + text: text.substring(0, nearest.index) + }; + elements.push(element); + } + if (nearestType) { + const element = { + type: nearestType, + text: nearest[1] + }; + if (nearestType === 'link' || nearestType === 'image') { + element.href = nearest[2]; + } + elements.push(element); + } + + // Remove the processed portion of the text + text = text.substring(nearest.index + (nearest[0] ? nearest[0].length : 0)); + } + + return elements; + } + + static processText(text) { + const elements = MarkdownProcessor.parseText(text); + console.log(text, elements.length); + return MarkdownProcessor.generateTextHtml(elements); + } + static generateHtml(elements) { const nodes = []; for (let element of elements) { + const parsedNodes = MarkdownProcessor.processText(element.text); + if (parsedNodes.length > 1) { + element.textNode = MarkdownTemplates.textWrapper(parsedNodes); + } else { + element.textNode = MarkdownTemplates.text(element.text); + } + switch (element.type) { case 'heading': - nodes.push(MarkdownTemplates.heading(element.text, element.level)); + nodes.push(MarkdownTemplates.heading(element.textNode, element.level)); break; case 'paragraph': - nodes.push(MarkdownTemplates.paragraph(element.text)); + nodes.push(MarkdownTemplates.paragraph(element.textNode)); break; - case 'link': - nodes.push(MarkdownTemplates.link(element.text, element.href)); + case 'code': + nodes.push(MarkdownTemplates.code(element.textNode)); break; - case 'image': - nodes.push(MarkdownTemplates.image(element.src, element.alt)); + case 'text': + nodes.push(MarkdownTemplates.text(element.text)); break; - case 'code': - nodes.push(MarkdownTemplates.code(element.text)); + case 'quote': + nodes.push(MarkdownTemplates.quote(element.textNode)); + break; + case 'listItem': + nodes.push(MarkdownTemplates.listItem(element.textNode)); + break; + case 'numberedList': + nodes.push(MarkdownTemplates.numberedListItem(element.textNode, element.number)); + break; + default: + nodes.push(MarkdownTemplates.paragraph(element.textNode)); break; + } + } + return nodes; + } + + static generateTextHtml(elements) { + const nodes = []; + for (let element of elements) { + switch (element.type) { case 'bold': nodes.push(MarkdownTemplates.bold(element.text)); break; @@ -120,17 +169,14 @@ export class MarkdownProcessor { case 'boldItalic': nodes.push(MarkdownTemplates.boldItalic(element.text)); break; - case 'quote': - nodes.push(MarkdownTemplates.quote(element.text)); - break; - case 'listItem': - nodes.push(MarkdownTemplates.listItem(element.text)); + case 'link': + nodes.push(MarkdownTemplates.link(element.text, element.href)); break; - case 'numberedList': - nodes.push(MarkdownTemplates.numberedListItem(element.text, element.number)); + case 'image': + nodes.push(MarkdownTemplates.image(element.src, element.alt)); break; default: - nodes.push(MarkdownTemplates.paragraph(element.text)); + nodes.push(MarkdownTemplates.text(element.text)); break; } } diff --git a/ui/templates/MarkdownTemplates.mjs b/ui/templates/MarkdownTemplates.mjs index a0e9ee4..6fba49f 100644 --- a/ui/templates/MarkdownTemplates.mjs +++ b/ui/templates/MarkdownTemplates.mjs @@ -4,14 +4,14 @@ export class MarkdownTemplates { static heading(text, level = 1) { return create(`h${level}`) .classes("markdown") - .text(text) + .children(text) .build(); } static paragraph(text) { return create("p") .classes("markdown") - .text(text) + .children(text) .build(); } @@ -63,21 +63,40 @@ export class MarkdownTemplates { static quote(text) { return create("blockquote") .classes("markdown") - .text(text) + .children(text) .build(); } static listItem(text) { return create("li") .classes("markdown") - .text(text) + .children(text) .build(); } static numberedListItem(text, number) { return create("li") .classes("markdown") - .text(`${number}. ${text}`) + .children( + create("span") + .classes("markdown") + .text(`${number}. `) + .build(), + text + ).build(); + } + + static textWrapper(children) { + return create("span") + .classes("markdown") + .children(children) + .build(); + } + + static text(text) { + return create("span") + .classes("markdown") + .text(text) .build(); } } \ No newline at end of file