fix: use same button position/style for playground back button

Instead of a separate back button, the Previous button is repurposed
as "Back" in playground mode - same position (left), same style.
Only the Next button is hidden in playground mode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
2026-01-16 14:58:25 +01:00
parent fb5fbe4107
commit 28d41344d1
4 changed files with 50 additions and 20 deletions

View File

@@ -149,7 +149,6 @@ const elements = {
expectedOverlay: document.getElementById("expected-overlay"),
previewWrapper: document.querySelector(".preview-wrapper"),
previewSection: document.querySelector(".preview-section"),
backBtn: document.getElementById("back-btn"),
prevBtn: document.getElementById("prev-btn"),
nextBtn: document.getElementById("next-btn"),
gameControls: document.querySelector(".game-controls"),
@@ -751,13 +750,18 @@ function updateNavigationButtons() {
const engineState = lessonEngine.getCurrentState();
const isPlayground = engineState.lesson?.mode === "playground";
// Hide nav buttons and center controls in playground mode, show back button
elements.backBtn?.classList.toggle("hidden", !isPlayground);
elements.prevBtn.classList.toggle("hidden", isPlayground);
// In playground mode: hide next button, repurpose prev as back button
elements.nextBtn.classList.toggle("hidden", isPlayground);
elements.gameControls?.classList.toggle("centered", isPlayground);
if (!isPlayground) {
if (isPlayground) {
// Change prev button to "Back" in playground mode
elements.prevBtn.textContent = t("back");
elements.prevBtn.disabled = false;
elements.prevBtn.classList.remove("btn-disabled");
} else {
// Normal mode: prev/next navigation
elements.prevBtn.textContent = t("previous");
elements.prevBtn.disabled = !engineState.canGoPrev;
elements.nextBtn.disabled = !engineState.canGoNext;
@@ -788,7 +792,16 @@ function nextLesson() {
}
function prevLesson() {
const prevModuleId = lessonEngine.getCurrentState().module?.id;
const engineState = lessonEngine.getCurrentState();
const isPlayground = engineState.lesson?.mode === "playground";
// In playground mode, "Back" navigates to home
if (isPlayground) {
navigateTo("#");
return;
}
const prevModuleId = engineState.module?.id;
const success = lessonEngine.previousLesson();
if (success) {
const newState = lessonEngine.getCurrentState();
@@ -2455,12 +2468,12 @@ function init() {
// Set timeout to show fallback if loading takes too long
loadingTimeout = setTimeout(showLoadingFallback, 3000);
// Load modules after editor is ready
// Handle OAuth callback FIRST (tokens are in URL hash, must run before router)
handleOAuthCallback().then(() => {
// Load modules (this also calls handleRoute inside)
initializeModules();
// Handle OAuth callback BEFORE router (tokens are in URL hash)
handleOAuthCallback().then(() => {
// Initialize URL router for shareable links
// Initialize URL router for browser back/forward
initRouter();
// Initialize authentication

View File

@@ -17,6 +17,7 @@ export async function handleOAuthCallback() {
// Check if hash contains OAuth tokens (access_token, error, etc.)
if (!hash.includes("access_token") && !hash.includes("error_description") && !hash.includes("refresh_token")) {
console.log("[Auth] No OAuth tokens in hash");
return false;
}
@@ -25,20 +26,33 @@ export async function handleOAuthCallback() {
try {
const supabaseModule = await import("./supabase.js");
if (!supabaseModule.isConfigured) {
console.log("[Auth] Supabase not configured");
return false;
}
// Let Supabase process the OAuth tokens
const { data, error } = await supabaseModule.auth.getSession();
// Parse tokens from hash
const params = new URLSearchParams(hash.substring(1));
const accessToken = params.get("access_token");
const refreshToken = params.get("refresh_token");
console.log("[Auth] Tokens found:", { accessToken: !!accessToken, refreshToken: !!refreshToken });
if (accessToken && refreshToken) {
// Explicitly set the session with tokens from URL
const { data, error } = await supabaseModule.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken
});
if (error) {
console.error("[Auth] OAuth callback error:", error.message);
console.error("[Auth] OAuth setSession error:", error.message);
} else if (data?.session) {
console.log("[Auth] OAuth login successful:", data.session.user?.email);
oauthHandled = true;
}
}
// Clear the hash after processing (will be replaced by router)
// Clear the hash after processing
window.history.replaceState(null, "", window.location.pathname);
return true;

View File

@@ -434,7 +434,6 @@
<!-- Right Panel: Preview + Navigation -->
<div class="right-panel">
<div class="game-controls">
<a id="back-btn" href="#" class="btn hidden" data-i18n="back">Back</a>
<button id="prev-btn" class="btn" data-i18n="previous">Previous</button>
<span class="module-pill" id="module-pill">
<span class="module-name"></span>

View File

@@ -45,6 +45,10 @@ export const auth = {
supabase?.auth.getSession() ??
Promise.resolve({ data: { session: null }, error: null }),
setSession: ({ access_token, refresh_token }) =>
supabase?.auth.setSession({ access_token, refresh_token }) ??
Promise.resolve({ data: { session: null }, error: { message: "Not configured" } }),
onAuthStateChange: (callback) =>
supabase?.auth.onAuthStateChange(callback) ?? { data: { subscription: { unsubscribe: () => {} } } },