Per-kind renderers, each a lazy split chunk: plain-iframe video/maps (wheel chains to the transcript; maps gate scroll behind ⌘), the in-document blockquote-script path for X/Instagram, the dark Spotify player, and the YouTube iframe. Adds Mermaid and DOMPurify-sanitised SVG fences and GFM alert callouts, all sized to 33dvh and theme-matched to avoid white color-scheme artifacts. Main-process stamps a Referer on YouTube embed requests.
33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import { cn } from '@/lib/utils'
|
|
|
|
/** Block wheel until ⌘/Ctrl so map embeds don't hijack transcript scroll. */
|
|
export function ScrollGate() {
|
|
const [active, setActive] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const sync = (event: KeyboardEvent) => setActive(event.metaKey || event.ctrlKey)
|
|
const clear = () => setActive(false)
|
|
|
|
window.addEventListener('keydown', sync)
|
|
window.addEventListener('keyup', sync)
|
|
window.addEventListener('blur', clear)
|
|
|
|
return () => {
|
|
window.removeEventListener('keydown', sync)
|
|
window.removeEventListener('keyup', sync)
|
|
window.removeEventListener('blur', clear)
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<div className={cn('group/gate absolute inset-0', active ? 'pointer-events-none' : 'pointer-events-auto')}>
|
|
<span className="pointer-events-none absolute bottom-2 left-2 rounded-md bg-black/55 px-1.5 py-0.5 text-[0.625rem] font-medium text-white opacity-0 transition-opacity group-hover/embed:opacity-100">
|
|
Hold ⌘ to zoom
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|