|
|
<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); |
|
|
|
|
|
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} |
|
|
|