From f1496e723254f9de7d037753859831d6933d5e9e Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Fri, 16 Jan 2026 02:28:12 +0100 Subject: [PATCH] feat: improve playground UX and fix undo/redo across lessons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add dice SVG icon for random template button - Reset button now restores last loaded template in playground - Clear editor history when switching lessons (prevents cross-lesson undo) - Add playground link to goodbye lesson - Center icon buttons with flexbox 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- lessons/99-goodbye.json | 2 +- public/dice.svg | 8 ++++++++ src/app.js | 18 ++++++++++++++---- src/impl/CodeEditor.js | 9 ++++++++- src/index.html | 2 +- src/main.css | 10 ++++++++++ 6 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 public/dice.svg diff --git a/lessons/99-goodbye.json b/lessons/99-goodbye.json index 8e0cf46..56cda1b 100644 --- a/lessons/99-goodbye.json +++ b/lessons/99-goodbye.json @@ -10,7 +10,7 @@ { "id": "next-steps", "title": "Keep Going!", - "description": "Great progress! You're building real web development skills.

Continue learning:
• MDN Web Docs - The definitive reference
• CSS-Tricks - Practical techniques

Practice ideas:
• Build your portfolio site
• Recreate a website you like
• Try the Playground to experiment freely

Contribute: Code Crispies is open source. Add lessons, fix bugs, or translate!", + "description": "Great progress! You're building real web development skills.

Continue learning:
• MDN Web Docs - The definitive reference
• CSS-Tricks - Practical techniques

Practice ideas:
• Build your portfolio site
• Recreate a website you like
• Try the Playground to experiment freely

Contribute: Code Crispies is open source. Add lessons, fix bugs, or translate!", "task": "Type Thank you!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; font-size: 1.5rem; color: #6366f1; }", diff --git a/public/dice.svg b/public/dice.svg new file mode 100644 index 0000000..55cd6af --- /dev/null +++ b/public/dice.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/app.js b/src/app.js index 91cc3ad..cda038e 100644 --- a/src/app.js +++ b/src/app.js @@ -27,7 +27,8 @@ const state = { skipResetCodeConfirmation: false }, showExpected: false, - animationTimeout: null + animationTimeout: null, + lastPlaygroundTemplate: null }; // Track CodeMirror views for cleanup @@ -579,9 +580,9 @@ function loadCurrentLesson() { lesson ); - // Set user code in CodeMirror + // Set user code in CodeMirror (clear history to prevent undo/redo across lessons) if (codeEditor) { - codeEditor.setValue(engineState.userCode); + codeEditor.setValueAndClearHistory(engineState.userCode); } // Update Run button text based on completion status @@ -731,9 +732,17 @@ function resetCode() { // Reset editor to initial code for current lesson lessonEngine.reset(); const engineState = lessonEngine.getCurrentState(); + const isPlayground = engineState.lesson?.mode === "playground"; track("reset_code", { module: engineState.module?.id, lesson: engineState.lessonIndex }); + if (codeEditor && engineState.lesson) { - codeEditor.setValue(engineState.lesson.initialCode || ""); + // In playground mode, restore the last template if available + if (isPlayground && state.lastPlaygroundTemplate) { + codeEditor.setValue(state.lastPlaygroundTemplate.code); + lessonEngine.applyUserCode(state.lastPlaygroundTemplate.code, true); + } else { + codeEditor.setValue(engineState.lesson.initialCode || ""); + } } // Clear hints and success indicators clearHint(); @@ -755,6 +764,7 @@ function loadRandomTemplate() { const template = getRandomTemplate(); if (codeEditor && template) { track("playground_template", { template: template.name }); + state.lastPlaygroundTemplate = template; codeEditor.setValue(template.code); // Apply the code to the preview lessonEngine.applyUserCode(template.code, true); diff --git a/src/impl/CodeEditor.js b/src/impl/CodeEditor.js index c39f089..6680fe2 100644 --- a/src/impl/CodeEditor.js +++ b/src/impl/CodeEditor.js @@ -183,7 +183,7 @@ export class CodeEditor { } /** - * Set editor value + * Set editor value (preserves history) */ setValue(value) { if (!this.view) return; @@ -197,6 +197,13 @@ export class CodeEditor { }); } + /** + * Set editor value and clear history (for lesson switching) + */ + setValueAndClearHistory(value) { + this.init(value); + } + /** * Set editor mode (html or css) */ diff --git a/src/index.html b/src/index.html index 8677d66..146342a 100644 --- a/src/index.html +++ b/src/index.html @@ -245,7 +245,7 @@ > ⟲ - + diff --git a/src/main.css b/src/main.css index 83111ec..8db786f 100644 --- a/src/main.css +++ b/src/main.css @@ -1154,6 +1154,9 @@ button.lesson-list-item { } .btn-icon { + display: flex; + align-items: center; + justify-content: center; padding: 4px 8px; font-size: 1rem; min-width: 32px; @@ -1168,6 +1171,13 @@ button.lesson-list-item { border-color: var(--primary-color); } +.btn-icon img { + width: 1rem; + height: 1rem; + margin: 0; + display: block; +} + .editor-tools { display: flex; gap: 4px;