From 663855f8a4e05d08a20594de8379690c5ba36262 Mon Sep 17 00:00:00 2001 From: Jens Date: Sun, 23 Aug 2026 15:00:30 +0200 Subject: [PATCH] Geef het tweede scherm zijn opmaak en lettertypen De analyseconsole opent in een eigen venster en stond daar in Times New Roman op wit, met standaard browserknoppen, terwijl de werkbank ernaast donker was. Niemand had er ooit naar gekeken. Er was code die de opmaak moest meenemen, maar die kloonde de link naar het nieuwe venster. Dat venster wordt geopend met window.open('') en staat dus op about:blank; nagemeten leverde link.sheet === null op. De regels worden nu ingeschreven in plaats van gelinkt, met de verwijzingen absoluut gemaakt tegen de bron van het blad. Daarna bleef document.fonts.status eeuwig op 'loading' staan. De bestanden kwamen wel binnen met status 200, maar in een about:blank-document voltooit het lettertypeladen niet. Een FontFace hangt niet aan een document, dus de gezichten die het hoofdvenster al geladen heeft worden nu overgenomen en gaan de @font-face-regels niet mee. Meetbaar: status 'loaded', en een schermafdruk die op de lettertypen wacht is klaar in 45 ms in plaats van af te breken na dertig seconden. Dit verklaarde ook de vijftien !important in dit blok: die probeerden iets te overschrijven dat er nooit aankwam. Alle zestien bleken overbodig en zijn in de vorige commit verwijderd. Co-Authored-By: Claude Opus 5 --- .../src/components/shell/SecondaryDisplay.tsx | 89 ++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) 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'