diff --git a/src/app.js b/src/app.js index 968c617..1263ee6 100644 --- a/src/app.js +++ b/src/app.js @@ -539,6 +539,7 @@ function resetSuccessIndicators() { elements.previewWrapper?.classList.remove("matched"); elements.previewWrapper?.classList.remove("completed-glow"); elements.previewSection?.classList.remove("matched"); + elements.previewSection?.classList.remove("completed-glow"); // Remove completion badge if present const badge = document.querySelector(".completion-badge"); @@ -661,8 +662,9 @@ function loadCurrentLesson() { elements.lessonTitleRow.appendChild(badge); } - // Show gradient border for completed lessons + // Show gradient border and glow for completed lessons elements.previewWrapper?.classList.add("completed-glow"); + elements.previewSection?.classList.add("completed-glow"); } else { elements.runBtn.querySelector("span").textContent = t("run"); @@ -670,6 +672,7 @@ function loadCurrentLesson() { const badge = document.querySelector(".completion-badge"); if (badge) badge.remove(); elements.previewWrapper?.classList.remove("completed-glow"); + elements.previewSection?.classList.remove("completed-glow"); } // Update level indicator (hide in playground mode) @@ -932,8 +935,9 @@ function runCode() { state.animationTimeout = setTimeout(() => { elements.previewWrapper?.classList.remove("matched"); elements.previewSection?.classList.remove("matched"); - // Keep the gradient border visible after animation + // Keep the gradient border and glow visible after animation elements.previewWrapper?.classList.add("completed-glow"); + elements.previewSection?.classList.add("completed-glow"); state.animationTimeout = null; }, 3500); diff --git a/src/auth.js b/src/auth.js index b27495b..a6e0586 100644 --- a/src/auth.js +++ b/src/auth.js @@ -1,5 +1,12 @@ import { t, applyTranslations } from "./i18n.js"; +// Analytics tracking helper +function track(eventName, eventData = {}) { + if (typeof umami !== "undefined" && umami.track) { + umami.track(eventName, eventData); + } +} + let currentUser = null; let oauthHandled = false; let lessonEngineRef = null; @@ -40,6 +47,8 @@ export async function handleOAuthCallback() { if (!error && data?.session) { oauthHandled = true; + const provider = data.session.user?.app_metadata?.provider || "oauth"; + track("auth_login", { method: provider }); } } diff --git a/src/main.css b/src/main.css index d409ce6..b4716ab 100644 --- a/src/main.css +++ b/src/main.css @@ -662,6 +662,18 @@ kbd { pointer-events: none; } +/* Persistent glow for completed lessons */ +.preview-section.completed-glow::before { + content: ""; + position: absolute; + inset: var(--spacing-md); + border-radius: var(--border-radius-md); + background: conic-gradient(from 0deg, #9163b8, #d45aa0, #1aafb8, #7c4dff, #9163b8); + filter: blur(30px); + opacity: 0.35; + pointer-events: none; +} + .preview-header { display: flex; justify-content: space-between; @@ -813,7 +825,7 @@ kbd { } 100% { --border-angle: -360deg; - opacity: 0; + opacity: 0.35; } }