diff --git a/static/assets/js/script.js b/static/assets/js/script.js index a4bbf12..0b618e0 100644 --- a/static/assets/js/script.js +++ b/static/assets/js/script.js @@ -1,5 +1,13 @@ const root = document.documentElement; const saved = window?.localStorage?.getItem('theme'); + +// Umami: safe track (no-op if script blocked or not loaded) +function umamiTrack(name, data) { + if (typeof window.umami !== 'undefined' && typeof window.umami.track === 'function') { + if (data != null) window.umami.track(name, data); + else window.umami.track(name); + } +} const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (saved) { @@ -27,6 +35,23 @@ if (themeToggle) { }); } +// Umami: scroll depth (25%, 50%, 75%, 100%) – once per milestone +const scrollMilestones = new Set(); +function onScroll() { + const doc = document.documentElement; + const scrollTop = doc.scrollTop || document.body.scrollTop; + const scrollHeight = (doc.scrollHeight || document.body.scrollHeight) - window.innerHeight; + if (scrollHeight <= 0) return; + const pct = Math.round((scrollTop / scrollHeight) * 100); + for (const m of [25, 50, 75, 100]) { + if (pct >= m && !scrollMilestones.has(m)) { + scrollMilestones.add(m); + umamiTrack('scroll-depth', { depth: String(m) }); + } + } +} +window.addEventListener('scroll', onScroll, { passive: true }); + // Lightbox (structure from Lightbox.svelte; we fill content and handle open/close) const dialog = document.querySelector('dialog.lightbox'); const lbContent = dialog?.querySelector('.lb-content'); @@ -71,6 +96,7 @@ function openLightbox(name, type, caption) { lbContent.replaceChildren(picture); } lbCaption.textContent = caption || ''; + umamiTrack('gallery-view', { name, type }); dialog.removeAttribute('inert'); dialog.setAttribute('aria-hidden', 'false'); dialog.showModal();