- Google kept showing the KantSites K for Elektro Bernhard after the site icon was changed. The own icon only existed at 32 and 180 px, and Google takes multiples of 48 only; /favicon.svg was a file in public/, so the web server always delivered the bundled K. - The icon is now also cut at 48 and 192 px. The head links /favicon-192.png, /favicon.ico serves the 48 px PNG, and /favicon.svg is answered by the application: the bundled mark without an own icon (now in public/assets/favicon.svg), otherwise a 302 to the 192 px PNG. Sites that set their icon earlier get the new sizes on first request. A removed icon no longer counts even though its files remain. - The archive update only adds files to public/, so it now deletes the old public/favicon.svg explicitly. - Seventh local reference site: the KANTARCI Immobilien landing page with a three-step rental request, own database, seed and reset scripts. Tests: 582 PHPUnit on PHP 8.2 and 8.4, 35 node, test:editor and test:browser green (test:browser reports only the known editor colour contrast on /grundlagen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
4.7 KiB
JavaScript
84 lines
4.7 KiB
JavaScript
/*
|
||
* Zeichnet die Bildkarten der KANTARCI-Mietanfrage und rastert sie zu PNG.
|
||
*
|
||
* Die Formularbausteine zeigen eine Auswahl als Bildkarten, sobald eine Option ein Bild aus der
|
||
* Mediathek hat - und die Mediathek nimmt kein SVG. Deshalb entstehen die Symbole hier als SVG
|
||
* und werden mit Chromium auf 800 × 600 (4:3 wie die Karte) gerastert. Stil der Landingpage:
|
||
* dünne Linie in Navy, runde Enden, ein goldener Akzent, warme helle Fläche dahinter.
|
||
*
|
||
* node scripts/build-kantarci-options.mjs → var/kantarci-immobilien-assets/options/*.png und *.svg
|
||
*/
|
||
import { chromium } from 'playwright';
|
||
import { mkdir, writeFile } from 'node:fs/promises';
|
||
|
||
const ziel = process.argv[2] || 'var/kantarci-immobilien-assets/options';
|
||
const NAVY = '#142535', GOLD = '#b49a66', HELL = '#f5f3ee', PAPIER = '#ffffff';
|
||
|
||
const bild = (inhalt) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" width="800" height="600">
|
||
<rect width="800" height="600" fill="${HELL}"/>
|
||
<g fill="none" stroke="${NAVY}" stroke-width="14" stroke-linecap="round" stroke-linejoin="round">${inhalt}</g>
|
||
</svg>`;
|
||
|
||
/** Eine Person: Kopf und Schultern. */
|
||
const person = (x, y, s = 1, farbe = NAVY) => `
|
||
<circle cx="${x}" cy="${y - 120 * s}" r="${46 * s}" stroke="${farbe}"/>
|
||
<path d="M${x - 88 * s} ${y + 70 * s}v-${30 * s}a${88 * s} ${88 * s} 0 0 1 ${176 * s} 0v${30 * s}" stroke="${farbe}"/>`;
|
||
|
||
/** Ein Grundriss: Außenwand, dazu Innenwände; der jeweils letzte Raum ist golden hinterlegt. */
|
||
const grundriss = (waende, akzent) => `
|
||
${akzent ? `<path d="${akzent}" fill="${GOLD}" fill-opacity=".22" stroke="none"/>` : ''}
|
||
<rect x="170" y="130" width="460" height="340" rx="8"/>
|
||
${waende}`;
|
||
|
||
const bilder = {
|
||
// Wer zieht ein?
|
||
'haushalt-allein': person(400, 330, 1.3),
|
||
'haushalt-zu-zweit': person(275, 330, 1.1) + person(525, 330, 1.1, GOLD),
|
||
'haushalt-familie': person(250, 320, 1.1) + person(550, 320, 1.1) + person(400, 400, .7, GOLD),
|
||
'haushalt-wg': person(200, 340, .9) + person(400, 340, .9, GOLD) + person(600, 340, .9),
|
||
|
||
// Wie viele Zimmer?
|
||
'zimmer-1': grundriss('<path d="M340 470v-50" />', 'M177 137h446v326H177z'),
|
||
'zimmer-2': grundriss('<path d="M400 130v230M400 420v50"/>', 'M407 137h216v326H407z'),
|
||
'zimmer-3': grundriss('<path d="M360 130v230M360 420v50M360 300h270"/>', 'M367 307h256v156H367z'),
|
||
'zimmer-4': grundriss('<path d="M400 130v340M170 300h170M460 300h170"/>', 'M407 307h216v156H407z')
|
||
+ `<path d="M690 110v80M650 150h80" stroke="${GOLD}"/>`,
|
||
|
||
// Was ist Ihnen wichtig?
|
||
'ausstattung-balkon': `
|
||
<path d="M250 110h300v230H250z"/><path d="M400 110v230"/>
|
||
<path d="M170 340h460M170 340v150M630 340v150M170 490h460" stroke="${GOLD}"/>
|
||
<path d="M245 340v150M320 340v150M400 340v150M480 340v150M555 340v150" stroke="${GOLD}" stroke-width="10"/>`,
|
||
'ausstattung-aufzug': `
|
||
<rect x="230" y="100" width="340" height="400" rx="12"/><path d="M400 100v400"/>
|
||
<path d="M620 210l40-50 40 50z" fill="${GOLD}" stroke="${GOLD}"/><path d="M620 300l40 50 40-50z" fill="${GOLD}" stroke="${GOLD}"/>`,
|
||
'ausstattung-stellplatz': `
|
||
<rect x="230" y="110" width="340" height="380" rx="40"/>
|
||
<path d="M340 420V180h80a60 60 0 0 1 0 120h-80" stroke="${GOLD}" stroke-width="22"/>`,
|
||
'ausstattung-kueche': `
|
||
<rect x="180" y="200" width="440" height="290" rx="10"/><path d="M180 270h440"/>
|
||
<circle cx="300" cy="235" r="14" fill="${NAVY}" stroke="none"/><circle cx="360" cy="235" r="14" fill="${NAVY}" stroke="none"/>
|
||
<rect x="250" y="320" width="300" height="120" rx="8" stroke="${GOLD}"/>
|
||
<path d="M300 140c0-30 30-30 30-60M400 140c0-30 30-30 30-60M500 140c0-30 30-30 30-60" stroke="${GOLD}" stroke-width="10"/>`,
|
||
'ausstattung-barrierefrei': `
|
||
<circle cx="380" cy="130" r="36" stroke="${GOLD}"/>
|
||
<path d="M380 180v130h120l50 110" stroke="${GOLD}"/><path d="M380 240h100"/>
|
||
<path d="M330 260a120 120 0 1 0 170 150"/>`,
|
||
'ausstattung-haustier': `
|
||
<path d="M400 330c-70 0-130 70-130 120 0 40 40 50 70 40s50-10 60-10 30 0 60 10 70 0 70-40c0-50-60-120-130-120z" fill="${GOLD}" fill-opacity=".25" stroke="${GOLD}"/>
|
||
<ellipse cx="260" cy="270" rx="42" ry="55"/><ellipse cx="350" cy="190" rx="42" ry="58"/>
|
||
<ellipse cx="450" cy="190" rx="42" ry="58"/><ellipse cx="540" cy="270" rx="42" ry="55"/>`,
|
||
};
|
||
|
||
await mkdir(ziel, { recursive: true });
|
||
const browser = await chromium.launch();
|
||
const page = await browser.newPage({ viewport: { width: 800, height: 600 }, deviceScaleFactor: 1 });
|
||
for (const [name, inhalt] of Object.entries(bilder)) {
|
||
const svg = bild(inhalt);
|
||
await writeFile(`${ziel}/${name}.svg`, svg);
|
||
await page.setContent(`<!doctype html><body style="margin:0;background:${PAPIER}">${svg}</body>`);
|
||
await page.locator('svg').screenshot({ path: `${ziel}/${name}.png`, omitBackground: false });
|
||
console.log('gezeichnet:', name);
|
||
}
|
||
await browser.close();
|