File size: 1,973 Bytes
b94860a
671ee32
ec5d85c
671ee32
6ee391c
 
ec5d85c
0c9aa36
b94860a
21b8785
 
4331e77
 
21b8785
 
4331e77
0c7ea0d
671ee32
ec5d85c
 
b94860a
ec5d85c
 
 
 
b94860a
 
6ee391c
 
671ee32
ec5d85c
 
 
 
0c9aa36
ec5d85c
 
 
 
6ee391c
ec5d85c
 
 
 
 
 
 
b3383d5
 
0c9aa36
ec5d85c
 
 
 
 
 
 
 
 
0c9aa36
ec5d85c
 
 
b94860a
 
 
3027088
5bb6125
4e7334f
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
<script lang="ts">
	import { processBlocks, processBlocksSync, type BlockToken } from "$lib/utils/marked";
	import MarkdownWorker from "$lib/workers/markdownWorker?worker";
	import MarkdownBlock from "./MarkdownBlock.svelte";
	import { browser } from "$app/environment";

	import { onMount, onDestroy } from "svelte";
	import { updateDebouncer } from "$lib/utils/updates";

	interface Props {
		content: string;
		sources?: { title?: string; link: string }[];
		loading?: boolean;
	}

	let { content, sources = [], loading = false }: Props = $props();

	let blocks: BlockToken[] = $state(processBlocksSync(content, sources));
	let worker: Worker | null = null;
	let latestRequestId = 0;

	function handleBlocks(result: BlockToken[], requestId: number) {
		if (requestId !== latestRequestId) return;
		blocks = result;
		updateDebouncer.endRender();
	}

	$effect(() => {
		if (!browser) {
			blocks = processBlocksSync(content, sources);
			return;
		}

		const requestId = ++latestRequestId;

		if (worker) {
			updateDebouncer.startRender();
			worker.postMessage({ type: "process", content, sources, requestId });
			return;
		}

		(async () => {
			updateDebouncer.startRender();
			const processed = await processBlocks(content, sources);
			// Only apply if this is still the latest request
			handleBlocks(processed, requestId);
		})();
	});

	onMount(() => {
		if (typeof Worker !== "undefined") {
			worker = new MarkdownWorker();
			worker.onmessage = (event: MessageEvent) => {
				const data = event.data as { type?: string; blocks?: BlockToken[]; requestId?: number };
				if (data?.type !== "processed" || !data.blocks || data.requestId === undefined) return;
				handleBlocks(data.blocks, data.requestId);
			};
		}
	});

	onDestroy(() => {
		worker?.terminate();
		worker = null;
	});
</script>

{#each blocks as block, index (loading && index === blocks.length - 1 ? `stream-${index}` : block.id)}
	<MarkdownBlock tokens={block.tokens} {loading} />
{/each}