MarkdownInlineTokens.svelte 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. <script lang="ts">
  2. import DOMPurify from 'dompurify';
  3. import { toast } from 'svelte-sonner';
  4. import type { Token } from 'marked';
  5. import { getContext } from 'svelte';
  6. const i18n = getContext('i18n');
  7. import { WEBUI_BASE_URL } from '$lib/constants';
  8. import { copyToClipboard, revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
  9. import Image from '$lib/components/common/Image.svelte';
  10. import KatexRenderer from './KatexRenderer.svelte';
  11. export let id: string;
  12. export let tokens: Token[];
  13. </script>
  14. {#each tokens as token}
  15. {#if token.type === 'escape'}
  16. {unescapeHtml(token.text)}
  17. {:else if token.type === 'html'}
  18. {@const html = DOMPurify.sanitize(token.text)}
  19. {#if html && html.includes('<video')}
  20. {@html html}
  21. {:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
  22. {@html `${token.text}`}
  23. {:else}
  24. {token.text}
  25. {/if}
  26. {:else if token.type === 'link'}
  27. <a href={token.href} target="_blank" rel="nofollow" title={token.title}>{token.text}</a>
  28. {:else if token.type === 'image'}
  29. <Image src={token.href} alt={token.text} />
  30. {:else if token.type === 'strong'}
  31. <strong>
  32. <svelte:self id={`${id}-strong`} tokens={token.tokens} />
  33. </strong>
  34. {:else if token.type === 'em'}
  35. <em>
  36. <svelte:self id={`${id}-em`} tokens={token.tokens} />
  37. </em>
  38. {:else if token.type === 'codespan'}
  39. <!-- svelte-ignore a11y-click-events-have-key-events -->
  40. <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
  41. <code
  42. class="codespan cursor-pointer"
  43. on:click={() => {
  44. copyToClipboard(unescapeHtml(token.text));
  45. toast.success($i18n.t('Copied to clipboard'));
  46. }}>{unescapeHtml(token.text)}</code
  47. >
  48. {:else if token.type === 'br'}
  49. <br />
  50. {:else if token.type === 'del'}
  51. <del>
  52. <svelte:self id={`${id}-del`} tokens={token.tokens} />
  53. </del>
  54. {:else if token.type === 'inlineKatex'}
  55. {#if token.text}
  56. <KatexRenderer content={revertSanitizedResponseContent(token.text)} displayMode={false} />
  57. {/if}
  58. {:else if token.type === 'iframe'}
  59. <iframe
  60. src="{WEBUI_BASE_URL}/api/v1/files/{token.fileId}/content"
  61. title={token.fileId}
  62. width="100%"
  63. frameborder="0"
  64. onload="this.style.height=(this.contentWindow.document.body.scrollHeight+20)+'px';"
  65. ></iframe>
  66. {:else if token.type === 'text'}
  67. {token.raw}
  68. {/if}
  69. {/each}