From 891cdb46c8687d2dcc2876a057ccab3cd96f1fdc Mon Sep 17 00:00:00 2001
From: thiloho <123883702+thiloho@users.noreply.github.com>
Date: Sat, 24 Aug 2024 20:34:06 +0200
Subject: [PATCH] Make markdown synchronous and add ability to toggle TOC
---
web-app/src/app.css | 16 ++++++-----
.../src/lib/components/WebsiteEditor.svelte | 6 +----
.../src/lib/templates/blog/BlogIndex.svelte | 6 +++--
web-app/src/lib/utils.ts | 27 ++++++++-----------
.../[websiteId]/publish/+page.server.ts | 8 +++---
web-app/template-styles/blog-styles.css | 4 +++
web-app/template-styles/common-styles.css | 9 +++++++
7 files changed, 43 insertions(+), 33 deletions(-)
diff --git a/web-app/src/app.css b/web-app/src/app.css
index 4c5d928..17a2f8c 100644
--- a/web-app/src/app.css
+++ b/web-app/src/app.css
@@ -1,7 +1,6 @@
button,
label,
select,
-summary,
[role="button"],
[role="option"],
label[for="toggle-mobile-preview"] {
@@ -14,7 +13,7 @@ textarea,
select,
a[role="button"],
label[for="toggle-mobile-preview"],
-summary {
+:not(.table-of-contents) > summary {
font: inherit;
color: inherit;
border: var(--border-primary);
@@ -47,22 +46,27 @@ a[role="button"] {
text-decoration: none;
}
-summary {
+:not(.table-of-contents) > summary {
max-inline-size: fit-content;
}
button,
a[role="button"],
label[for="toggle-mobile-preview"],
-summary {
+:not(.table-of-contents) > summary {
background-color: var(--bg-secondary);
}
-:is(button, a[role="button"], label[for="toggle-mobile-preview"], summary):hover {
+:is(
+ button,
+ a[role="button"],
+ label[for="toggle-mobile-preview"],
+ :not(.table-of-contents) > summary
+ ):hover {
background-color: var(--bg-tertiary);
}
-:is(a, button, input, textarea, select, summary):focus,
+:is(button, input, textarea, select):focus,
#toggle-mobile-preview:checked + label {
outline: 0.125rem solid var(--color-accent);
outline-offset: 0.25rem;
diff --git a/web-app/src/lib/components/WebsiteEditor.svelte b/web-app/src/lib/components/WebsiteEditor.svelte
index 6722f8d..4d0c07e 100644
--- a/web-app/src/lib/components/WebsiteEditor.svelte
+++ b/web-app/src/lib/components/WebsiteEditor.svelte
@@ -56,11 +56,7 @@
{#if fullPreview}
{:else}
- {#await md(previewContent)}
-
Loading preview...
- {:then content}
- {@html content}
- {/await}
+ {@html md(previewContent)}
{/if}
diff --git a/web-app/src/lib/templates/blog/BlogIndex.svelte b/web-app/src/lib/templates/blog/BlogIndex.svelte
index 2659c89..5852c4a 100644
--- a/web-app/src/lib/templates/blog/BlogIndex.svelte
+++ b/web-app/src/lib/templates/blog/BlogIndex.svelte
@@ -34,8 +34,10 @@
{@html mainContent}
{#if articles.length > 0}
-
- Articles
+
+
{#each articles as article}
diff --git a/web-app/src/lib/utils.ts b/web-app/src/lib/utils.ts
index 3a297d2..96d28c7 100644
--- a/web-app/src/lib/utils.ts
+++ b/web-app/src/lib/utils.ts
@@ -13,18 +13,18 @@ export const sortOptions = [
export const ALLOWED_MIME_TYPES = ["image/jpeg", "image/png", "image/svg+xml", "image/webp"];
-const createMarkdownParser = () => {
+const createMarkdownParser = (showToc = true) => {
const marked = new Marked();
marked.use({
- async: true,
+ async: false,
pedantic: false,
gfm: true
});
marked.use(
markedHighlight({
- async: true,
+ async: false,
langPrefix: "language-",
highlight(code, lang) {
const language = hljs.getLanguage(lang) ? lang : "plaintext";
@@ -52,7 +52,7 @@ const createMarkdownParser = () => {
let headings: { text: string; raw: string; level: number; id: string }[] = [];
let sectionStack: { level: number; id: string }[] = [];
- function gfmHeadingId({ prefix = "" } = {}) {
+ function gfmHeadingId({ prefix = "", showToc = true } = {}) {
return {
renderer: {
heading(this: Renderer, { tokens, depth }: { tokens: Token[]; depth: number }) {
@@ -65,14 +65,12 @@ const createMarkdownParser = () => {
const heading = { level, text, id, raw };
headings.push(heading);
- // Close any sections that are at a higher level than the current heading
let closingSections = "";
while (sectionStack.length > 0 && sectionStack[sectionStack.length - 1].level >= level) {
sectionStack.pop();
closingSections += "
";
}
- // Open a new section for this heading
sectionStack.push({ level, id });
const openingSection = ``;
@@ -94,13 +92,11 @@ const createMarkdownParser = () => {
return src;
},
postprocess(html: string) {
- // Close any remaining open sections
const closingRemainingSection = "".repeat(sectionStack.length);
- // Generate table of contents
const tableOfContents =
- headings.length > 0
- ? `
+ showToc && headings.length > 0
+ ? `
Table of contents
${headings
@@ -123,17 +119,16 @@ const createMarkdownParser = () => {
};
}
- marked.use(gfmHeadingId());
+ marked.use(gfmHeadingId({ showToc: showToc }));
return marked;
};
-const marked = createMarkdownParser();
+export const md = (markdownContent: string, showToc = true) => {
+ const marked = createMarkdownParser(showToc);
+ const html = marked.parse(markdownContent);
-export const md = async (markdownContent: string) => {
- const html = await marked.parse(markdownContent);
-
- return html;
+ return html as string;
};
export const handleImagePaste = async (event: ClipboardEvent, API_BASE_PREFIX: string) => {
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
index a14327f..435dece 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
@@ -60,7 +60,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
title: websiteData.title,
logoType: websiteData.logo_type,
logo: websiteData.logo_text,
- mainContent: await md(websiteData.main_content ?? ""),
+ mainContent: md(websiteData.main_content ?? "", false),
articles: websiteData.articles ?? [],
footerAdditionalText: websiteData.additional_text ?? ""
}
@@ -74,7 +74,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
title: websiteData.title,
logoType: websiteData.logo_type,
logo: websiteData.logo_text,
- mainContent: await md(websiteData.main_content ?? ""),
+ mainContent: md(websiteData.main_content ?? "", false),
articles: websiteData.articles ?? [],
footerAdditionalText: websiteData.additional_text ?? ""
}
@@ -117,7 +117,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
: "",
publicationDate: article.publication_date,
- mainContent: await md(article.main_content ?? ""),
+ mainContent: md(article.main_content ?? ""),
footerAdditionalText: websiteData.additional_text ?? ""
}
}));
@@ -134,7 +134,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
: "",
publicationDate: article.publication_date,
- mainContent: await md(article.main_content ?? ""),
+ mainContent: md(article.main_content ?? ""),
footerAdditionalText: websiteData.additional_text ?? ""
}
}));
diff --git a/web-app/template-styles/blog-styles.css b/web-app/template-styles/blog-styles.css
index 9e91dbf..a367443 100644
--- a/web-app/template-styles/blog-styles.css
+++ b/web-app/template-styles/blog-styles.css
@@ -30,3 +30,7 @@ footer {
flex-direction: column;
gap: var(--space-xs);
}
+
+.table-of-contents {
+ margin-block-end: var(--space-s);
+}
diff --git a/web-app/template-styles/common-styles.css b/web-app/template-styles/common-styles.css
index 8ce63fd..308faae 100644
--- a/web-app/template-styles/common-styles.css
+++ b/web-app/template-styles/common-styles.css
@@ -210,3 +210,12 @@ td {
padding-block: var(--space-3xs);
border: var(--border-primary);
}
+
+summary {
+ cursor: pointer;
+}
+
+:is(a, summary):focus {
+ outline: 0.125rem solid var(--color-accent);
+ outline-offset: 0.25rem;
+}