nsarrazin HF Staff commited on
Commit
6f93b29
·
1 Parent(s): 020f8bb

fix: markdown rendering

Browse files
src/lib/components/chat/MarkdownRenderer.svelte CHANGED
@@ -2,7 +2,7 @@
2
  import type { WebSearchSource } from "$lib/types/WebSearch";
3
  import katex from "katex";
4
  import DOMPurify from "isomorphic-dompurify";
5
- import { marked, type MarkedOptions } from "marked";
6
  import CodeBlock from "../CodeBlock.svelte";
7
 
8
  export let content: string;
@@ -30,24 +30,6 @@
30
  });
31
  }
32
 
33
- const renderer = new marked.Renderer();
34
-
35
- // For code blocks with simple backticks
36
- renderer.codespan = (code) => {
37
- // Unsanitize double-sanitized code
38
- return `<code>${code.replaceAll("&amp;", "&")}</code>`;
39
- };
40
-
41
- renderer.link = (href, title, text) => {
42
- return `<a href="${href?.replace(/>$/, "")}" target="_blank" rel="noreferrer">${text}</a>`;
43
- };
44
-
45
- const options: MarkedOptions = {
46
- gfm: true,
47
- // breaks: true,
48
- renderer,
49
- };
50
-
51
  function escapeHTML(content: string) {
52
  return content.replace(
53
  /[<>&\n]/g,
@@ -60,8 +42,6 @@
60
  );
61
  }
62
 
63
- $: tokens = marked.lexer(addInlineCitations(content, sources));
64
-
65
  function processLatex(parsed: string) {
66
  const delimiters = [
67
  { left: "$$", right: "$$", display: true },
@@ -98,6 +78,21 @@
98
  return parsed;
99
  }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  DOMPurify.addHook("afterSanitizeAttributes", (node) => {
102
  if (node.tagName === "A") {
103
  node.setAttribute("rel", "noreferrer");
@@ -106,17 +101,20 @@
106
  });
107
  </script>
108
 
109
- {#each tokens as token}
110
- {#if token.type === "code"}
111
- <CodeBlock lang={token.lang} code={token.text} />
112
- {:else}
113
- {@const parsed = marked.parse(processLatex(escapeHTML(token.raw)), options)}
114
- {#await parsed then parsed}
115
- <!-- eslint-disable-next-line svelte/no-at-html-tags -->
116
- {@html DOMPurify.sanitize(parsed)}
117
- {/await}
118
- {/if}
119
- {/each}
 
 
 
120
 
121
  <style lang="postcss">
122
  :global(.katex-display) {
 
2
  import type { WebSearchSource } from "$lib/types/WebSearch";
3
  import katex from "katex";
4
  import DOMPurify from "isomorphic-dompurify";
5
+ import { Marked } from "marked";
6
  import CodeBlock from "../CodeBlock.svelte";
7
 
8
  export let content: string;
 
30
  });
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  function escapeHTML(content: string) {
34
  return content.replace(
35
  /[<>&\n]/g,
 
42
  );
43
  }
44
 
 
 
45
  function processLatex(parsed: string) {
46
  const delimiters = [
47
  { left: "$$", right: "$$", display: true },
 
78
  return parsed;
79
  }
80
 
81
+ const marked = new Marked({
82
+ hooks: {
83
+ preprocess: (md) => addInlineCitations(escapeHTML(md), sources),
84
+ postprocess: (html) => {
85
+ return DOMPurify.sanitize(processLatex(html));
86
+ },
87
+ },
88
+ renderer: {
89
+ codespan: (code) => `<code>${code.replaceAll("&amp;", "&")}</code>`,
90
+ link: (href, title, text) =>
91
+ `<a href="${href?.replace(/>$/, "")}" target="_blank" rel="noreferrer">${text}</a>`,
92
+ },
93
+ gfm: true,
94
+ });
95
+
96
  DOMPurify.addHook("afterSanitizeAttributes", (node) => {
97
  if (node.tagName === "A") {
98
  node.setAttribute("rel", "noreferrer");
 
101
  });
102
  </script>
103
 
104
+ <div
105
+ class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
106
+ >
107
+ {#each marked.lexer(content) as token}
108
+ {#if token.type === "code"}
109
+ <CodeBlock lang={token.lang} code={token.text} />
110
+ {:else}
111
+ {#await marked.parse(token.raw) then parsed}
112
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
113
+ {@html parsed}
114
+ {/await}
115
+ {/if}
116
+ {/each}
117
+ </div>
118
 
119
  <style lang="postcss">
120
  :global(.katex-display) {