Heatmap in Geraeterahmen (Computer/Tablet + Handy) und Keks statt "Datenschutz-Einstellungen"

Heatmap im Portal (Funktionen -> Analyse): die echte Website laeuft jetzt in realistischen
Geraeterahmen, gross MacBook oder iPad (Umschalter oben), daneben das iPhone, jeweils in echter
Breite (1512 / 820 / 402 px), verkleinert und im Geraet scrollbar, mit den Klicks der
passenden Geraeteklasse als Overlay. MacBook und iPhone stammen aus den Rahmen der neuen
Website (device-mockups), das iPad ist im gleichen Stil nachgebaut (public/geraete.css/.js).

- heatmap-link: embed: true haengt __wk_embed=1 an; w.js reicht das als data-embed an h.js.
- h.js eingebettet (nur in einem Rahmen): keine Leiste, Links und Formulare gesperrt (kein
  Seitenwechsel, also keine Zaehlung und kein Banner), Scrollleisten aus, Klickzahl per
  postMessage nur an die Herkunft von h.js (project.webklar.com).
- iframe mit Sandbox allow-scripts allow-same-origin (keine Formulare, Pop-ups, keine
  Navigation des Portals); Laden erst, wenn die Heatmap in Sicht kommt. Laesst sich eine
  Website nicht einbetten, kommt nach 20 s ein Hinweis mit "In neuem Tab oeffnen" (alter Weg).

Banner-Skript: der Einstellungsknopf nach der Wahl ist jetzt ein angeknabberter Keks unten
rechts (Inline-SVG, 44 px). Er dreht sich langsam, haelt beim Darueberfahren/Fokus an und
zeigt dann "Datenschutz-Einstellungen" (auch sein Name fuer Screenreader, passend zum
Bannertext); bei reduzierter Bewegung dreht er sich nicht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-02 15:09:17 +00:00
1 parent ff7fe35c06
commit 0a113c2983
14 files changed
+1658 -91

No files matched your search

+3 -2
View File
@@ -247,7 +247,7 @@ router.get('/analyse/stats', analyseGuard, async (req, res) => {
router.post('/analyse/heatmap-link', analyseGuard, async (req, res) => {
try {
const { projectId, path, vp } = req.body || {}
const { projectId, path, vp, embed } = req.body || {}
if (typeof path !== 'string' || !path.startsWith('/') || path.length > 300 || /[?#\\\s\u0000-\u001f\u007f]/.test(path)) {
return res.status(400).json({ error: 'Ungültiger Pfad' })
}
@@ -260,7 +260,8 @@ router.post('/analyse/heatmap-link', analyseGuard, async (req, res) => {
if (!domains.length) return res.status(409).json({ error: 'Keine gültige Domain' })
const host = project.liveDomain ? domains[domains.length - 1] : domains[0]
const token = analyseDeps.client.heatToken({ siteKey: doc.siteKey, path, vp })
res.json({ url: `https://${host}${path}?__wk_heat=${encodeURIComponent(token)}` })
// embed: Ansicht im Geraeterahmen des Portals (h.js ohne Leiste, Links gesperrt)
res.json({ url: `https://${host}${path}?__wk_heat=${encodeURIComponent(token)}${embed === true ? '&__wk_embed=1' : ''}` })
} catch (err) {
fail(res, err, 'Der Heatmap-Link konnte gerade nicht erstellt werden.')
}