From 5289bbf29b4fa1d0cdaedcea8b22dc21f27a9f10 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Mon, 16 Mar 2026 15:04:02 +0100 Subject: [PATCH] Tutorial: Spotlight und Bubble auf sichtbaren Viewport-Bereich beschraenken - Spotlight wird auf den sichtbaren Teil des Elements geclippt statt ueber den Viewport hinauszuragen - Bubble-Position nutzt sichtbaren Elementbereich statt voller Rect - Demo-Summary-Text gekuerzt, damit er in die Lagebild-Kachel passt - Behebt das Problem, dass bei grossen Kacheln (Lagebild, Timeline, Karte) die Sprechblase ausserhalb des sichtbaren Bereichs landete Co-Authored-By: Claude Opus 4.6 (1M context) --- src/static/dashboard.html | 2 +- src/static/js/tutorial.js | 78 ++++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 2abf9d2..a5b0deb 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -764,7 +764,7 @@ - + diff --git a/src/static/js/tutorial.js b/src/static/js/tutorial.js index b3d38f3..c164f30 100644 --- a/src/static/js/tutorial.js +++ b/src/static/js/tutorial.js @@ -49,19 +49,18 @@ const Tutorial = { + '' + '', - _DEMO_SUMMARY: '

Am Morgen des 16. März 2026 kam es im Hamburger Hafen zu einer schweren Explosion ' - + 'in einem Containerterminal am Burchardkai. Nach übereinstimmenden Berichten von ' - + 'dpa [1], ' - + 'Reuters [2] und ' - + 'NDR [3] ' - + 'ereignete sich die Detonation gegen 06:45 Uhr Ortszeit in einem Lagerbereich für Gefahrgut.

' - + '

Die Hamburger Feuerwehr ist mit einem Großaufgebot vor Ort. Laut ' - + 'Hamburger Abendblatt [4] ' - + 'wurden mindestens 12 Personen verletzt, davon 3 schwer. Die Ursache der Explosion ist noch unklar. ' - + 'Die Polizei Hamburg hat den Bereich weiträumig abgesperrt.

' - + '

Der Hamburger Hafen, Europas drittgrößter Seehafen, hat den Betrieb im betroffenen Terminal vorübergehend ' - + 'eingestellt. Auswirkungen auf den Schiffsverkehr werden derzeit geprüft ' - + 'HPA [5].

', + _DEMO_SUMMARY: '

Am 16. März 2026 kam es im Hamburger Hafen zu einer Explosion ' + + 'im Containerterminal Burchardkai ' + + '[1] ' + + '[2]. ' + + 'Die Detonation ereignete sich gegen 06:45 Uhr in einem Gefahrgutbereich. ' + + 'Mindestens 12 Personen wurden verletzt ' + + '[3].

' + + '

Die Feuerwehr ist mit einem Großaufgebot vor Ort. Der Hafenbetrieb im betroffenen Terminal wurde ' + + 'vorübergehend eingestellt ' + + '[4]. ' + + 'Die Ursache ist noch unklar ' + + '[5].

', _DEMO_FACTCHECKS: [ { status: 'confirmed', icon: '✓', claim: 'Eine Explosion ereignete sich am 16.03.2026 gegen 06:45 Uhr im Hamburger Hafen.', sources: 5 }, @@ -901,13 +900,21 @@ const Tutorial = { } var rect = el.getBoundingClientRect(); + var vw = window.innerWidth; + var vh = window.innerHeight; var pad = 8; - var s = this._els.spotlight.style; - s.top = (rect.top - pad) + 'px'; - s.left = (rect.left - pad) + 'px'; - s.width = (rect.width + pad * 2) + 'px'; - s.height = (rect.height + pad * 2) + 'px'; + // Sichtbaren Bereich berechnen (auf Viewport beschränkt) + var top = Math.max(rect.top, 0) - pad; + var left = Math.max(rect.left, 0) - pad; + var bottom = Math.min(rect.bottom, vh) + pad; + var right = Math.min(rect.right, vw) + pad; + + var s = this._els.spotlight.style; + s.top = top + 'px'; + s.left = left + 'px'; + s.width = (right - left) + 'px'; + s.height = (bottom - top) + 'px'; s.borderRadius = '8px'; s.opacity = '1'; }, @@ -988,6 +995,14 @@ const Tutorial = { var vh = window.innerHeight; var gap = 16; + // Sichtbaren Bereich des Elements berechnen (auf Viewport beschraenkt) + var visTop = Math.max(rect.top, 0); + var visBottom = Math.min(rect.bottom, vh); + var visLeft = Math.max(rect.left, 0); + var visRight = Math.min(rect.right, vw); + var visCenterY = (visTop + visBottom) / 2; + var visCenterX = (visLeft + visRight) / 2; + // Reset transform bubble.style.transform = ''; bubble.style.width = bw + 'px'; @@ -996,30 +1011,33 @@ const Tutorial = { var pos = step.position || 'bottom'; var bubbleHeight = 300; - if (pos === 'bottom' && (rect.bottom + gap + bubbleHeight > vh)) pos = 'top'; - if (pos === 'top' && (rect.top - gap - bubbleHeight < 0)) pos = 'bottom'; - if (pos === 'right' && (rect.right + gap + bw > vw)) pos = 'left'; - if (pos === 'left' && (rect.left - gap - bw < 0)) pos = 'right'; + if (pos === 'bottom' && (visBottom + gap + bubbleHeight > vh)) pos = 'top'; + if (pos === 'top' && (visTop - gap - bubbleHeight < 0)) pos = 'bottom'; + if (pos === 'right' && (visRight + gap + bw > vw)) pos = 'left'; + if (pos === 'left' && (visLeft - gap - bw < 0)) pos = 'right'; bubble.className = 'tutorial-bubble visible tutorial-pos-' + pos; + // Bubble-Top immer im sichtbaren Bereich halten + var clampTop = function(t) { return Math.max(8, Math.min(t, vh - bubbleHeight - 8)); }; + switch (pos) { case 'bottom': - bubble.style.top = (rect.bottom + gap) + 'px'; - bubble.style.left = Math.max(8, Math.min(rect.left + rect.width / 2 - bw / 2, vw - bw - 8)) + 'px'; + bubble.style.top = Math.min(visBottom + gap, vh - bubbleHeight - 8) + 'px'; + bubble.style.left = Math.max(8, Math.min(visCenterX - bw / 2, vw - bw - 8)) + 'px'; break; case 'top': - bubble.style.top = (rect.top - gap) + 'px'; - bubble.style.left = Math.max(8, Math.min(rect.left + rect.width / 2 - bw / 2, vw - bw - 8)) + 'px'; + bubble.style.top = Math.max(visTop - gap, 8) + 'px'; + bubble.style.left = Math.max(8, Math.min(visCenterX - bw / 2, vw - bw - 8)) + 'px'; bubble.style.transform = 'translateY(-100%)'; break; case 'right': - bubble.style.top = Math.max(8, rect.top + rect.height / 2 - bubbleHeight / 2) + 'px'; - bubble.style.left = (rect.right + gap) + 'px'; + bubble.style.top = clampTop(visCenterY - bubbleHeight / 2) + 'px'; + bubble.style.left = Math.min(visRight + gap, vw - bw - 8) + 'px'; break; case 'left': - bubble.style.top = Math.max(8, rect.top + rect.height / 2 - bubbleHeight / 2) + 'px'; - bubble.style.left = (rect.left - gap - bw) + 'px'; + bubble.style.top = clampTop(visCenterY - bubbleHeight / 2) + 'px'; + bubble.style.left = Math.max(visLeft - gap - bw, 8) + 'px'; break; } },