Files
tolaria/src/components/tableOfContentsWorkerClient.ts

77 lines
2.0 KiB
TypeScript
Raw Normal View History

2026-05-04 12:37:30 +02:00
import { buildTableOfContentsFromMarkdownOnly, type TocItem } from './tableOfContentsModel'
export const TOC_BUILD_DEBOUNCE_MS = 180
interface WorkerRequest {
entryTitle: string
markdown: string
requestId: number
}
interface WorkerResponse {
requestId: number
toc: TocItem
}
interface PendingRequest {
reject: (reason?: unknown) => void
resolve: (toc: TocItem) => void
}
let nextRequestId = 1
let tocWorker: Worker | null = null
const pendingRequests = new Map<number, PendingRequest>()
function rejectPendingRequests(reason: unknown) {
pendingRequests.forEach(({ reject }) => {
reject(reason)
})
2026-05-04 12:37:30 +02:00
pendingRequests.clear()
}
function buildTocWithoutWorker(entryTitle: string, markdown: string): Promise<TocItem> {
return new Promise((resolve) => {
window.setTimeout(() => {
resolve(buildTableOfContentsFromMarkdownOnly(entryTitle, markdown))
}, 0)
})
}
function handleWorkerMessage(event: MessageEvent<WorkerResponse>) {
const pending = pendingRequests.get(event.data.requestId)
if (!pending) return
pendingRequests.delete(event.data.requestId)
pending.resolve(event.data.toc)
}
function handleWorkerError(event: ErrorEvent) {
rejectPendingRequests(event.error ?? event.message)
tocWorker?.terminate()
tocWorker = null
}
function activeTocWorker(): Worker | null {
if (typeof Worker === 'undefined') return null
if (tocWorker) return tocWorker
tocWorker = new Worker(new URL('./tableOfContents.worker.ts', import.meta.url), { type: 'module' })
tocWorker.onmessage = handleWorkerMessage
tocWorker.onerror = handleWorkerError
return tocWorker
}
export function buildTableOfContentsInWorker(entryTitle: string, markdown: string): Promise<TocItem> {
const worker = activeTocWorker()
if (!worker) return buildTocWithoutWorker(entryTitle, markdown)
return new Promise((resolve, reject) => {
const requestId = nextRequestId
nextRequestId += 1
pendingRequests.set(requestId, { resolve, reject })
const request: WorkerRequest = { entryTitle, markdown, requestId }
worker.postMessage(request)
})
}