diff --git a/frontend/src/components/shell/SecondaryDisplay.tsx b/frontend/src/components/shell/SecondaryDisplay.tsx index 4616bec3..24282b5c 100644 --- a/frontend/src/components/shell/SecondaryDisplay.tsx +++ b/frontend/src/components/shell/SecondaryDisplay.tsx @@ -18,14 +18,100 @@ function defaultGeometry(): SecondaryDisplayGeometry { } } +/** + * Neemt de opmaak van het hoofdvenster mee naar de console. + * + * De vorige versie kloonde de naar het nieuwe venster. + * Dat venster wordt geopend met window.open('') en is dus about:blank; daar + * werd de link wel in de head gezet maar nooit opgehaald — nagemeten leverde + * link.sheet === null op. Gevolg: de console stond volledig onopgemaakt, in + * Times New Roman op wit, terwijl de werkbank ernaast donker was. Het verklaart + * ook waarom de paneelregels voor dit venster met !important stonden: die + * probeerden iets te overschrijven dat er nooit aankwam. Sinds de opmaak hier + * wel aankomt zijn ze overbodig gebleken en verwijderd. + * + * Nu worden de regels zelf ingeschreven. Dat is dezelfde oorsprong, dus + * cssRules is leesbaar, en er komt geen netwerkverzoek aan te pas. Lukt het + * lezen toch niet, dan valt hij terug op de gekloonde link. + */ function copyDocumentStyles(target: Document): void { + const regels: string[] = [] + let alleenGelezen = true + + // Verwijzingen in de regels — lettertypen, iconen, achtergronden — staan + // relatief. Het nieuwe venster is about:blank en heeft dus geen basis om ze + // tegen op te lossen; zonder deze stap blijft het wachten op lettertypen die + // nooit aankomen. Ze worden hier absoluut gemaakt tegen de bron van het blad. + const maakAbsoluut = (tekst: string, basis: string): string => + tekst.replace(/url\((['"]?)([^'")]+)\1\)/g, (heel, quote, verwijzing) => { + if (/^(data:|blob:|https?:|\/\/)/i.test(verwijzing)) return heel + try { + return `url("${new URL(verwijzing, basis).href}")` + } catch { + return heel + } + }) + + // @font-face gaat bewust niet mee. De bestanden komen in dit venster wel + // binnen met status 200, maar de FontFace springt nooit naar 'loaded': in een + // document dat op about:blank staat voltooit het lettertypeladen niet. Het + // gevolg was dat document.fonts.status eeuwig op 'loading' bleef en de console + // kort in terugvalletters opende. De gezichten worden in plaats daarvan + // overgenomen uit het hoofdvenster, waar ze al geladen zijn — zie + // copyLoadedFonts hieronder. + const isFontFace = (regel: CSSRule): boolean => + typeof CSSFontFaceRule !== 'undefined' && regel instanceof CSSFontFaceRule + + for (const sheet of Array.from(document.styleSheets)) { + try { + const basis = sheet.href ?? document.baseURI + const tekst = Array.from(sheet.cssRules) + .filter((regel) => !isFontFace(regel)) + .map((regel) => maakAbsoluut(regel.cssText, basis)) + .join('\n') + if (tekst) regels.push(tekst) + } catch { + alleenGelezen = false + } + } + + if (regels.length > 0) { + const stijl = target.createElement('style') + stijl.setAttribute('data-herkomst', 'hoofdvenster') + stijl.textContent = regels.join('\n') + target.head.append(stijl) + } + + if (alleenGelezen && regels.length > 0) return + + // Terugval voor bladen die niet te lezen zijn, bijvoorbeeld van een ander domein. document.head.querySelectorAll('link[rel="stylesheet"], style').forEach((node) => { const clone = node.cloneNode(true) as HTMLLinkElement | HTMLStyleElement - if (clone instanceof HTMLLinkElement) clone.href = node instanceof HTMLLinkElement ? node.href : '' + if (clone instanceof HTMLLinkElement && node instanceof HTMLLinkElement) clone.href = node.href target.head.append(clone) }) } +/** + * Neemt de al geladen lettertypegezichten van het hoofdvenster over. + * + * Een FontFace hangt niet aan een document, dus een gezicht dat hier al + * ingeladen is kan rechtstreeks aan de FontFaceSet van het nieuwe venster + * worden toegevoegd. Dat scheelt niet alleen een tweede download, het omzeilt + * ook dat het laden in een about:blank-document nooit voltooit. + */ +function copyLoadedFonts(target: Window): void { + const doel = target.document.fonts + if (!doel || typeof document.fonts === 'undefined') return + document.fonts.forEach((gezicht) => { + try { + doel.add(gezicht) + } catch { + // Al aanwezig, of dit gezicht laat zich niet overdragen. + } + }) +} + function initialiseSecondaryDocument(target: Window, onClose: () => void): HTMLElement { const targetDocument = target.document targetDocument.title = 'GeoIntel · Analyseconsole' @@ -37,6 +123,7 @@ function initialiseSecondaryDocument(target: Window, onClose: () => void): HTMLE viewport.content = 'width=device-width, initial-scale=1' targetDocument.head.append(viewport) copyDocumentStyles(targetDocument) + copyLoadedFonts(target) const shell = targetDocument.createElement('div') shell.className = 'secondary-display-shell'