forked from jan/psslides
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { domToSvgDataUrl } from './dom-to-image'
|
|
|
|
export interface CodeToElementOptions {
|
|
title?: string
|
|
allowScripts?: boolean
|
|
styles?: string[]
|
|
}
|
|
|
|
export function codeToElement(code: string, options: CodeToElementOptions = {}): HTMLIFrameElement {
|
|
const iframe = document.createElement('iframe')
|
|
const sandbox = new Set<string>()
|
|
|
|
if (!options.allowScripts) {
|
|
sandbox.add('allow-same-origin')
|
|
} else {
|
|
sandbox.add('allow-scripts')
|
|
}
|
|
|
|
iframe.title = options.title ?? 'Rendered code'
|
|
iframe.setAttribute('sandbox', Array.from(sandbox).join(' '))
|
|
iframe.srcdoc = `<!doctype html><html><head><meta charset="UTF-8"><style>html,body{margin:0;min-height:100%;}</style>${(options.styles ?? []).join('\n')}</head><body>${code}</body></html>`
|
|
|
|
return iframe
|
|
}
|
|
|
|
export async function createCodeThumbnail(
|
|
code: string,
|
|
options: CodeToElementOptions = {}
|
|
): Promise<HTMLImageElement> {
|
|
const iframe = codeToElement(code, { title: 'Code thumbnail', ...options, allowScripts: false })
|
|
iframe.style.width = '640px'
|
|
iframe.style.height = '360px'
|
|
iframe.style.position = 'fixed'
|
|
iframe.style.left = '-10000px'
|
|
iframe.style.border = '0'
|
|
document.body.append(iframe)
|
|
|
|
try {
|
|
await new Promise<void>((resolve, reject) => {
|
|
iframe.addEventListener('load', () => resolve(), { once: true })
|
|
iframe.addEventListener(
|
|
'error',
|
|
() => reject(new Error('Unable to render code thumbnail')),
|
|
{ once: true }
|
|
)
|
|
})
|
|
|
|
const content = iframe.contentDocument?.body
|
|
const thumbnail = document.createElement('img')
|
|
if (content) {
|
|
thumbnail.src = domToSvgDataUrl(content, { width: 640, height: 360 })
|
|
}
|
|
return thumbnail
|
|
} finally {
|
|
iframe.remove()
|
|
}
|
|
}
|