Skip to content

Commit 12bf30b

Browse files
feat: use async markdown parsing (#210)
Update +page.svelte
1 parent f63784b commit 12bf30b

File tree

4 files changed

+55
-13
lines changed

4 files changed

+55
-13
lines changed

src/frontend/src/lib/functions/markdown.ts renamed to src/frontend/src/lib/markdown/markdown.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import rehypeStringify from 'rehype-stringify';
33
import remarkParse from 'remark-parse';
44
import remarkRehype from 'remark-rehype';
55
import { unified } from 'unified';
6+
import rehypeEmailMangle from './plugins/email-obfuscate';
67

7-
export function html_to_markdown(markdown: string) {
8-
const result = unified()
8+
export async function html_to_markdown(markdown: string) {
9+
const result = await unified()
910
.use(remarkParse)
1011
.use(remarkRehype) // Markdown → HTML AST
12+
.use(rehypeEmailMangle) // Obfuscate email addresses
1113
.use(rehypeSanitize) // Sanitize HTML AST
1214
.use(rehypeStringify) // HTML AST → string
13-
.processSync(markdown);
15+
.process(markdown);
1416
return result.toString();
1517
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Inspired by : https://github.com/markedjs/marked-mangle/blob/9484ff1fe551b8b8bef497a360890225943ace82/src/index.js
2+
import type { Element, Root, Text } from 'hast';
3+
import type { Plugin } from 'unified';
4+
import { visit } from 'unist-util-visit';
5+
6+
/**
7+
* Rehype plugin to obfuscate email addresses
8+
*/
9+
const rehypeEmailMangle: Plugin<[], Root> = () => {
10+
return (tree: Root) => {
11+
// Visit text nodes only
12+
visit(tree, 'text', (node: Text) => {
13+
node.value = node.value.replace(/([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/gi, (email) =>
14+
email
15+
.split('')
16+
.map((c) => `&#${c.charCodeAt(0)};`)
17+
.join('')
18+
);
19+
});
20+
21+
// Visit element nodes only
22+
visit(tree, 'element', (node: Element) => {
23+
if (node.tagName === 'a' && node.properties?.href) {
24+
const href = node.properties.href;
25+
if (typeof href === 'string' && href.startsWith('mailto:')) {
26+
const email = href.slice(7);
27+
node.properties.href = `mailto:${email
28+
.split('')
29+
.map((c) => `&#${c.charCodeAt(0)};`)
30+
.join('')}`;
31+
}
32+
}
33+
});
34+
35+
return tree;
36+
};
37+
};
38+
39+
export default rehypeEmailMangle;

src/frontend/src/routes/(needs_onboarding)/(login_required)/admin/config/+page.svelte

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,16 @@
1414
import { formatSeconds, secondsToNumber, T_UNITS, type TimeUnit } from '#functions/times';
1515
import { sanitizeExt } from '#functions/sanitize';
1616
import { Separator } from '$lib/components/ui/separator';
17-
import { html_to_markdown } from '#functions/markdown';
17+
import { html_to_markdown } from '$lib/markdown/markdown';
1818
1919
// Query hook
2020
const { config: configQuery, update_config } = useConfigQuery();
2121
2222
// Derived state
2323
let configData = $derived(configQuery.data);
2424
let descDraft = $state('');
25-
let previewHtml = $derived(
26-
descDraft
27-
? String(html_to_markdown(descDraft))
28-
: html_to_markdown(configData?.site_description ?? '')
29-
);
25+
let previewMarkdown = $derived(descDraft? String(descDraft): configData.site_description ?? "")
26+
3027
3128
// UI state
3229
let editing = $state<
@@ -652,7 +649,9 @@
652649
in:fade
653650
class="prose max-w-none p-8 text-sm leading-relaxed prose-zinc dark:prose-invert"
654651
>
655-
{@html previewHtml}
652+
{#await html_to_markdown(previewMarkdown) then html}
653+
{@html html}
654+
{/await}
656655
</div>
657656
{/if}
658657
</Card.Content>

src/frontend/src/routes/(needs_onboarding)/(navbar_and_footer)/+page.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import { cn } from '$lib/utils';
3333
import { toast } from 'svelte-sonner';
3434
import { dev } from '$app/environment';
35-
import { html_to_markdown } from '#functions/markdown';
35+
import { html_to_markdown } from '$lib/markdown/markdown';
3636
3737
const { config: configData } = useConfigQuery();
3838
@@ -63,7 +63,7 @@
6363
// Encryption progress states
6464
let encryptionProgress = $state(0);
6565
let isEncrypting = $state(false);
66-
let renderedDetails = $derived(html_to_markdown(configData.data?.site_description ?? ''));
66+
let detailsMarkdown = $derived(configData.data?.site_description ?? '');
6767
6868
$effect(() => {
6969
const total = files.reduce((sum, file) => sum + file.size, 0);
@@ -869,7 +869,9 @@
869869
<div
870870
class="prose w-full max-w-none prose-zinc md:text-sm lg:text-lg lg:leading-relaxed dark:prose-invert"
871871
>
872-
{@html renderedDetails}
872+
{#await html_to_markdown(detailsMarkdown) then html}
873+
{@html html}
874+
{/await}
873875
</div>
874876
</ScrollArea>
875877
</div>

0 commit comments

Comments
 (0)