Files
KantSites/scripts/build-kantarci-options.mjs
T
kantarciandClaude Opus 5 45d1141951 Show the own site icon to Google under every address, and the KANTARCI landing page
- 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>
2026-09-21 07:37:04 +02:00

84 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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();