import { toPng, toSvg } from 'html-to-image'
export type ExportQuality = 'standard' | 'high' | 'ultra'
export type ExportFormat = 'png' | 'svg'
export type ExportBackground = 'dark' | 'white'
export const EXPORT_QUALITY_OPTIONS: { value: ExportQuality; label: string; pixelRatio: number; hint: string }[] = [
{ value: 'standard', label: 'Standard', pixelRatio: 1, hint: '1× — small file' },
{ value: 'high', label: 'High', pixelRatio: 2, hint: '2× — recommended' },
{ value: 'ultra', label: 'Ultra', pixelRatio: 4, hint: '4× — print quality, large file' },
]
export const EXPORT_BACKGROUND_OPTIONS: { value: ExportBackground; label: string; color: string; hint: string }[] = [
{ value: 'dark', label: 'Dark', color: '#0d1117', hint: 'screen / docs' },
{ value: 'white', label: 'White', color: '#ffffff', hint: 'printing' },
]
function backgroundColor(background: ExportBackground): string {
return (EXPORT_BACKGROUND_OPTIONS.find((o) => o.value === background) ?? EXPORT_BACKGROUND_OPTIONS[0]).color
}
// The `.react-flow` element paints its own opaque background (colorMode dark),
// and html-to-image's `backgroundColor` option only shows through transparent
// areas. Force the chosen colour directly on the live element for the duration
// of the capture, then restore whatever was there before.
async function withBackground(
element: HTMLElement,
color: string,
capture: () => Promise,
): Promise {
const previous = element.style.backgroundColor
element.style.backgroundColor = color
try {
return await capture()
} finally {
element.style.backgroundColor = previous
}
}
export async function exportToPng(
element: HTMLElement,
quality: ExportQuality = 'high',
background: ExportBackground = 'dark',
): Promise {
const option = EXPORT_QUALITY_OPTIONS.find((o) => o.value === quality) ?? EXPORT_QUALITY_OPTIONS[1]
const color = backgroundColor(background)
const dataUrl = await withBackground(element, color, () =>
toPng(element, {
backgroundColor: color,
pixelRatio: option.pixelRatio,
}),
)
triggerDownload(dataUrl, 'homelable-canvas.png')
}
export async function exportToSvg(
element: HTMLElement,
background: ExportBackground = 'dark',
): Promise {
const color = backgroundColor(background)
const dataUrl = await withBackground(element, color, () =>
toSvg(element, {
backgroundColor: color,
}),
)
triggerDownload(dataUrl, 'homelable-canvas.svg')
}
function triggerDownload(dataUrl: string, filename: string): void {
const link = document.createElement('a')
link.download = filename
link.href = dataUrl
// Firefox only triggers a programmatic click when the anchor is in the DOM.
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}