Spaces:
Running
Running
File size: 1,978 Bytes
b39afbe |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
/**
* Copyright (c) 2023 MERCENARIES.AI PTE. LTD.
* All rights reserved.
*/
import { ChatRenderer } from 'omni-client-services';
import DOMPurify from 'dompurify';
import { marked, type MarkedOptions } from 'marked';
import { markedEmoji } from 'marked-emoji';
// An extension to render markdown in a chat message
class MarkdownRenderer extends ChatRenderer {
// opts: https://marked.js.org/using_advanced
constructor(
id?: string,
opts?: { marked?: MarkedOptions; markedEmoji?: { emojis: NonNullable<object>; unicode?: boolean } }
) {
opts ??= {};
opts.marked ??= { mangle: false, gfm: true, breaks: true, headerIds: false, headerPrefix: undefined };
opts.markedEmoji ??= { unicode: true, emojis: { emojis: { fire: '🔥', heart: '❤️', thumbsup: '👍' } } };
super({ id: id ?? 'text/markdown' }, opts);
}
async load(): Promise<void> {
marked.use(markedEmoji(this.opts.markedEmoji));
}
render(content: { type: string; value: any }): string {
// Convert content.value to string
// If it's an array, join it using Markdown paragraph breaks
const text = Array.isArray(content.value) ? content.value.join('\n\n') : content.value?.toString();
const markdownWithHtml = marked.parse(text, { mangle: false, headerIds: false });
const sanitizedHtml = DOMPurify.sanitize(markdownWithHtml, {
ALLOWED_TAGS: [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'blockquote',
'p',
'a',
'ul',
'ol',
'nl',
'li',
'b',
'i',
'strong',
'em',
'strike',
'code',
'hr',
'br',
'div',
'table',
'thead',
'caption',
'tbody',
'tr',
'th',
'td',
'pre',
'img'
],
ALLOWED_ATTR: ['href', 'alt', 'src', 'title']
});
return sanitizedHtml;
}
}
export default MarkdownRenderer;
|