diff --git a/plugs/markdown/assets/markdown_widget.css b/plugs/markdown/assets/markdown_widget.css index 0090b0e..92e06e6 100644 --- a/plugs/markdown/assets/markdown_widget.css +++ b/plugs/markdown/assets/markdown_widget.css @@ -25,10 +25,29 @@ body { color: var(--root-color); } -ul li p { - margin: 0; +ul, +ol { + margin-top: 0; + margin-bottom: 0; } +ul { + list-style: none; +} + +ul li::before { + content: "\2022"; + /* Add content: \2022 is the CSS Code/unicode for a bullet */ + color: var(--editor-list-bullet-color); + display: inline-block; + /* Needed to add space between the bullet and the text */ + width: 1em; + /* Also needed for space (tweak if needed) */ + margin-left: -1em; + /* Also needed for space (tweak if needed) */ +} + + body:hover #button-bar, body:active #button-bar { display: block; diff --git a/plugs/markdown/html_render.ts b/plugs/markdown/html_render.ts index 14d09e5..1a9a55e 100644 --- a/plugs/markdown/html_render.ts +++ b/plugs/markdown/html_render.ts @@ -7,10 +7,18 @@ export type Tag = { } | string; function htmlEscape(s: string): string { - return s.replace(/&/g, "&") + s = s.replace(/&/g, "&") .replace(//g, ">") - .replace(/"/g, """); + .replace(/"/g, """) + .replace(/\n/g, "
"); + + let oldS = s; + do { + oldS = s; + s = s.replace(/ /g, "  "); + } while (s !== oldS); + return s; } export function renderHtml(t: Tag | null): string { @@ -33,9 +41,9 @@ export function renderHtml(t: Tag | null): string { if (t.name === Fragment) { return body; } - if (t.body) { - return `<${t.name}${attrs}>${body}`; - } else { - return `<${t.name}${attrs}/>`; - } + // if (t.body) { + return `<${t.name}${attrs}>${body}`; + // } else { + // return `<${t.name}${attrs}/>`; + // } } diff --git a/plugs/markdown/markdown_render.test.ts b/plugs/markdown/markdown_render.test.ts index 5b06ade..78a475b 100644 --- a/plugs/markdown/markdown_render.test.ts +++ b/plugs/markdown/markdown_render.test.ts @@ -29,17 +29,37 @@ Deno.test("Markdown render", async () => { await system.unloadAll(); }); -Deno.test("Smart hard break test", async () => { +Deno.test("Smart hard break test", () => { const example = `**Hello** *world!*`; const lang = buildMarkdown([]); const tree = parse(lang, example); - const html = renderMarkdownToHtml(tree, { + const html = renderMarkdownToHtml(tree, { failOnUnknown: true, smartHardBreak: true, }); - assertEquals( - html, - `

Hello
world!

`, - ); + // assertEquals( + // html, + // `Hello
world!
`, + // ); + + const example2 = `This is going to be a text. With a new line. + +And another + +* and a list +* with a second item + +### [[Bla]] + Url: something + Server: something else + 📅 last_updated - [Release notes](release_notes_url)`; + + const tree2 = parse(lang, example2); + const html2 = renderMarkdownToHtml(tree2, { + failOnUnknown: true, + smartHardBreak: true, + }); + + console.log(html2); }); diff --git a/plugs/markdown/markdown_render.ts b/plugs/markdown/markdown_render.ts index 992d7f7..2095ab8 100644 --- a/plugs/markdown/markdown_render.ts +++ b/plugs/markdown/markdown_render.ts @@ -1,4 +1,5 @@ import { + addParentPointers, collectNodesOfType, findNodeOfType, ParseTree, @@ -17,9 +18,12 @@ type MarkdownRenderOptions = { translateUrls?: (url: string) => string; }; -function cleanTags(values: (Tag | null)[]): Tag[] { +function cleanTags(values: (Tag | null)[], cleanWhitespace = false): Tag[] { const result: Tag[] = []; for (const value of values) { + if (cleanWhitespace && typeof value === "string" && value.match(/^\s+$/)) { + continue; + } if (value) { result.push(value); } @@ -28,12 +32,13 @@ function cleanTags(values: (Tag | null)[]): Tag[] { } function preprocess(t: ParseTree, options: MarkdownRenderOptions = {}) { + addParentPointers(t); traverseTree(t, (node) => { - if (node.type === "Paragraph" && options.smartHardBreak) { - for (const child of node.children!) { - // If at the paragraph level there's a newline, let's turn it into a hard break - if (!child.type && child.text === "\n") { - child.type = "HardBreak"; + if (!node.type) { + if (node.text?.startsWith("\n")) { + const prevNodeIdx = node.parent!.children!.indexOf(node) - 1; + if (node.parent!.children![prevNodeIdx]?.type !== "Paragraph") { + node.text = node.text.slice(1); } } } @@ -109,7 +114,10 @@ function render( }; case "Paragraph": return { - name: "p", + name: "span", + attrs: { + class: "p", + }, body: cleanTags(mapRender(t.children!)), }; // Code blocks @@ -167,17 +175,17 @@ function render( case "BulletList": return { name: "ul", - body: cleanTags(mapRender(t.children!)), + body: cleanTags(mapRender(t.children!), true), }; case "OrderedList": return { name: "ol", - body: cleanTags(mapRender(t.children!)), + body: cleanTags(mapRender(t.children!), true), }; case "ListItem": return { name: "li", - body: cleanTags(mapRender(t.children!)), + body: cleanTags(mapRender(t.children!), true), }; case "StrongEmphasis": return { diff --git a/web/styles/editor.scss b/web/styles/editor.scss index 190c292..3a34aeb 100644 --- a/web/styles/editor.scss +++ b/web/styles/editor.scss @@ -392,7 +392,6 @@ font-weight: normal; margin-bottom: -3rem; overflow: auto; - } table {