MarkdownInlineTokens.svelte 2.6 KB

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