import { createPortal } from 'react-dom' import { useState, useEffect, useId } from 'react' import { X, ExternalLink, FileText, Loader2 } from 'lucide-react' import { getAuthUrl } from '../../api/authUrl' import { openFile } from '../../utils/fileDownload' import { useTranslation } from '../../i18n' import { DialogHeader, DialogShell, DialogTile, NEUTRAL_TINT, PILL, fs } from '../shared/DialogShell' import { Tooltip } from '../shared/Tooltip' import type { NoteFile } from './CollabNotes.types' // ── File Preview Portal ───────────────────────────────────────────────────── interface FilePreviewPortalProps { file: NoteFile | null onClose: () => void } /** The round buttons over the dark image backdrop. */ const LIGHTBOX_BTN = 'grid h-9 w-9 place-items-center rounded-full bg-[rgba(255,255,255,0.12)] text-[rgba(255,255,255,0.85)] hover:bg-[rgba(255,255,255,0.22)]' // theme-lint-disable: the lightbox is dark in every scheme export function FilePreviewPortal({ file, onClose }: FilePreviewPortalProps) { const { t } = useTranslation() const labelId = useId() const [authUrl, setAuthUrl] = useState('') const rawUrl = file?.url || '' useEffect(() => { setAuthUrl('') if (!rawUrl) return void getAuthUrl(rawUrl, 'download').then(setAuthUrl) }, [rawUrl]) const isImage = !!file?.mime_type?.startsWith('image/') // The dialog handles its own Escape; the image lightbox takes it here. useEffect(() => { if (!file || !isImage) return const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() } window.addEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey) }, [file, isImage, onClose]) if (!file) return null const isPdf = file.mime_type === 'application/pdf' const isTxt = file.mime_type?.startsWith('text/') const openInNewTab = () => openFile(rawUrl).catch(() => {}) if (isImage) { return createPortal(