Files
code-crispies/src/index.html
Michael Czechowski 6c65381fcb feat: add Guided Learning Paths feature
Implement PathManager to orchestrate multi-module learning journeys:
- Add PathManager class with start/pause/resume functionality
- Create learning-paths.json config with CSS Fundamentals path
- Integrate path progress tracking with LessonEngine
- Add path selection UI to homepage and navigation
- Include JSON schema for learning path validation
- Add comprehensive test suite for PathManager
2026-01-12 20:30:09 +01:00

346 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="./favicon.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Code Crispies - Free, open-source platform for learning HTML and CSS through hands-on exercises. Master semantic markup, selectors, flexbox, animations and more."
/>
<title>Code Crispies - Learn HTML & CSS Interactively</title>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<a href="#main-content" class="skip-link" data-i18n="skipLink">Skip to main content</a>
<div class="app-container">
<header class="header">
<button id="menu-btn" class="menu-toggle" data-i18n-aria-label="menuOpen" aria-label="Open menu">
<span class="hamburger-icon"></span>
</button>
<a href="#" id="logo-link" class="logo">
<img src="./bowl.png" width="40" alt="CODE CRISPIES Logo" />
<h1><span class="code-text">CODE</span><span>CRISPIES</span></h1>
</a>
<div class="header-actions">
<button id="path-indicator" class="path-indicator" style="display: none;" aria-label="Current learning path">
<span class="path-indicator-name"></span>
<span class="path-indicator-progress"></span>
</button>
<button id="help-btn" class="help-toggle" data-i18n-aria-label="help" aria-label="Help">?</button>
</div>
</header>
<main class="game-layout" id="main-content">
<!-- Left Panel: Instructions + Editor -->
<div class="left-panel">
<section class="instructions">
<h2 id="lesson-title"></h2>
<div class="task-instruction" id="task-instruction"></div>
<div class="lesson-description" id="lesson-description"></div>
<details class="concept-section" id="concept-section">
<summary class="concept-summary" data-i18n="whyThisWorks">Why This Works</summary>
<div class="concept-content">
<div class="concept-explanation" id="concept-explanation"></div>
<div class="concept-diagram" id="concept-diagram"></div>
<div class="concept-container-vs-item" id="concept-container-vs-item"></div>
</div>
</details>
</section>
<section class="editor-section">
<div class="code-editor">
<div class="editor-header">
<label for="code-input" class="editor-label" data-i18n="editorLabel">CSS Editor</label>
<div class="editor-actions">
<div class="editor-tools">
<button id="undo-btn" class="btn btn-icon" data-i18n-title="undoTitle" title="Undo (Ctrl+Z)"></button>
<button id="redo-btn" class="btn btn-icon" data-i18n-title="redoTitle" title="Redo (Ctrl+Shift+Z)"></button>
<button
id="reset-code-btn"
class="btn btn-icon"
data-i18n-title="resetCodeTitle"
title="Reset to initial code"
>
</button>
</div>
<button id="run-btn" class="btn btn-run"><img src="./gear.svg" alt="" /><span data-i18n="run">Run</span></button>
</div>
</div>
<div class="editor-content">
<textarea id="code-input" class="code-input" spellcheck="false" autocomplete="off" style="display: none"></textarea>
</div>
</div>
<div class="hint-area" id="hint-area"></div>
</section>
</div>
<!-- Right Panel: Preview + Navigation -->
<div class="right-panel">
<div class="game-controls">
<button id="prev-btn" class="btn" data-i18n="previous">Previous</button>
<span class="module-pill" id="module-pill">
<span class="module-name"></span>
<span class="level-indicator" id="level-indicator"></span>
</span>
<button id="next-btn" class="btn btn-primary" data-i18n="next">Next</button>
<button id="next-in-path-btn" class="btn btn-path" style="display: none;" data-i18n="nextInPath">Next in Path</button>
</div>
<div class="preview-section">
<div class="preview-wrapper">
<div class="preview-frame" id="preview-area"></div>
<div class="expected-overlay" id="expected-overlay">
<div class="expected-frame" id="preview-expected"></div>
</div>
</div>
<div class="preview-header">
<span class="preview-label" data-i18n="yourOutput">Your Output</span>
<button id="show-expected-btn" class="btn btn-small" data-i18n="showExpected">Show Expected</button>
</div>
</div>
</div>
</main>
<!-- Sidebar Backdrop -->
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
<!-- Slide-out Sidebar -->
<aside class="sidebar-drawer" id="sidebar-drawer" data-i18n-aria-label="menu" aria-label="Menu">
<div class="sidebar-header">
<h3 data-i18n="menu">Menu</h3>
<button id="close-sidebar" class="close-btn" data-i18n-aria-label="closeMenu" aria-label="Close menu">&times;</button>
</div>
<div class="sidebar-section">
<h4 data-i18n="progress">Progress</h4>
<div class="progress-display" id="progress-display">
<div class="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
<span class="progress-text" id="progress-text">0% Complete</span>
</div>
</div>
<nav class="sidebar-section" aria-label="Lesson navigation">
<h4 id="lessons-heading" data-i18n="lessons">Lessons</h4>
<div class="module-list" id="module-list" role="tree" aria-labelledby="lessons-heading"></div>
</nav>
<div class="sidebar-section">
<h4 data-i18n="learningPaths">Learning Paths</h4>
<div id="path-progress-display" class="path-progress-display" style="display: none;">
<div class="path-progress-info">
<div class="path-progress-name"></div>
<div class="path-progress-stats"></div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="path-progress-fill"></div>
</div>
</div>
<button id="view-paths-btn" class="btn btn-text" data-i18n="viewAllPaths">View All Paths</button>
</div>
<div class="sidebar-section">
<h4 data-i18n="settings">Settings</h4>
<label class="setting-row">
<span class="setting-label" data-i18n="language">Language</span>
<select id="lang-select" class="lang-select">
<option value="en">English</option>
<option value="de">Deutsch</option>
<option value="pl">Polski</option>
<option value="es">Español</option>
<option value="ar">العربية</option>
<option value="uk">Українська</option>
</select>
</label>
<label class="toggle-switch">
<input type="checkbox" id="disable-feedback-toggle" checked />
<span class="toggle-slider"></span>
<span class="toggle-label" data-i18n="showHints">Show Hints</span>
</label>
<button id="reset-btn" class="btn btn-text" data-i18n="resetAllProgress">Reset All Progress</button>
</div>
<footer class="app-footer">
<span data-i18n="openSource">Open Source:</span>
<a href="https://git.librete.ch/libretech/code-crispies" target="_blank">Gitea</a>
<span data-i18n="by">by</span> <a href="https://librete.ch" title="LibreTECH">LibreTECH</a>
</footer>
</aside>
<!-- Help Dialog -->
<dialog id="help-dialog" class="dialog">
<div class="dialog-header">
<h3 data-i18n="helpTitle">Help</h3>
<button id="help-dialog-close" class="dialog-close" aria-label="Close">&times;</button>
</div>
<div class="dialog-content">
<h4 data-i18n="aboutTitle">About Code Crispies</h4>
<p data-i18n="aboutText">
Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required -
just start coding!
</p>
<h4 data-i18n="learningModesTitle">Learning Modes</h4>
<ul>
<li data-i18n-html="modeCss"><strong>CSS</strong> - Write CSS rules to style elements</li>
<li data-i18n-html="modeTailwind"><strong>Tailwind</strong> - Apply utility classes directly in HTML</li>
<li data-i18n-html="modeHtml"><strong>HTML</strong> - Practice semantic markup and native elements</li>
</ul>
<h4 data-i18n="gettingStartedTitle">Getting Started</h4>
<p data-i18n="gettingStartedText">
Open the menu (☰) to browse lesson modules. Each module covers a specific topic with progressive exercises.
</p>
<h4 data-i18n="completingLessonsTitle">Completing Lessons</h4>
<ol>
<li data-i18n="completingStep1">Read the task instructions on the left</li>
<li data-i18n="completingStep2">Write your code in the editor</li>
<li data-i18n="completingStep3">Watch the live preview update as you type</li>
<li data-i18n="completingStep4">Follow hints to fix any issues</li>
<li data-i18n-html="completingStep5">Click <strong>Next</strong> when complete</li>
</ol>
<h4 data-i18n="editorToolsTitle">Editor Tools</h4>
<ul>
<li data-i18n-html="editorToolUndo"><strong>↶ Undo</strong> / <strong>↷ Redo</strong> - Navigate edit history</li>
<li data-i18n-html="editorToolReset"><strong>⟲ Reset</strong> - Restore initial code for current lesson</li>
<li data-i18n-html="editorToolExpected"><strong>Show Expected</strong> - Toggle the target result overlay</li>
</ul>
<h4 data-i18n="keyboardShortcutsTitle">Keyboard Shortcuts</h4>
<ul>
<li data-i18n-html="shortcutUndo"><kbd>Ctrl+Z</kbd> - Undo</li>
<li data-i18n-html="shortcutRedo"><kbd>Ctrl+Shift+Z</kbd> - Redo</li>
</ul>
<h4 data-i18n="emmetTitle">Emmet Shortcuts (HTML mode)</h4>
<p data-i18n-html="emmetText">Type abbreviations and press <kbd>Tab</kbd> to expand:</p>
<ul>
<li data-i18n-html="emmetClass"><kbd>div.box</kbd> → div with class</li>
<li data-i18n-html="emmetChildren"><kbd>ul>li*3</kbd> → ul with 3 li children</li>
<li data-i18n-html="emmetNested"><kbd>form>input+button</kbd> → nested structure</li>
<li data-i18n-html="emmetContent"><kbd>p{Hello}</kbd> → p with text content</li>
</ul>
<h4 data-i18n="moreProjectsTitle">More Projects</h4>
<div class="project-cards">
<a href="https://nextlevelshit.github.io/html-over-js/" target="_blank" class="project-card">
<strong>HTML over JS</strong>
<span data-i18n="htmlOverJsDesc"> - Learn to leverage native HTML elements instead of custom JavaScript solutions</span>
</a>
<a href="https://nextlevelshit.github.io/web-engineering-mandala/" target="_blank" class="project-card">
<strong>Web Engineering Mandala</strong>
<span data-i18n="mandalaDesc"> - Interactive visualization of JavaScript technologies organized by complexity</span>
</a>
</div>
<h4 data-i18n="contactTitle">Contact & Links</h4>
<p data-i18n-html="contactText">Code Crispies is developed by <a href="https://librete.ch" target="_blank">LibreTECH</a></p>
<ul>
<li><a href="https://git.librete.ch/libretech/code-crispies" target="_blank">Gitea</a> Self-hosted source repository</li>
<li><a href="https://github.com/nextlevelshit/code-crispies" target="_blank">GitHub</a> Public mirror</li>
<li><a href="https://www.linkedin.com/in/michael-werner-czechowski" target="_blank">LinkedIn</a> Michael Czechowski</li>
</ul>
</div>
</dialog>
<!-- Reset Code Confirmation Dialog -->
<dialog id="reset-code-dialog" class="dialog">
<div class="dialog-header">
<h3 data-i18n="resetCodeDialogTitle">Reset Code</h3>
<button id="reset-code-dialog-close" class="dialog-close" aria-label="Close">&times;</button>
</div>
<div class="dialog-content">
<p data-i18n="resetCodeDialogText">Reset your code to the initial state for this lesson?</p>
<label class="toggle-switch" style="margin: 1rem 0">
<input type="checkbox" id="reset-code-dont-show" />
<span class="toggle-slider"></span>
<span class="toggle-label" data-i18n="dontShowAgain">Don't show this again</span>
</label>
<div class="dialog-actions">
<button id="cancel-reset-code" class="btn" data-i18n="cancel">Cancel</button>
<button id="confirm-reset-code" class="btn btn-ghost" data-i18n="reset">Reset</button>
</div>
</div>
</dialog>
<!-- Reset Confirmation Dialog -->
<dialog id="reset-dialog" class="dialog">
<div class="dialog-header">
<h3 data-i18n="resetDialogTitle">Reset Progress</h3>
<button id="reset-dialog-close" class="dialog-close" aria-label="Close">&times;</button>
</div>
<div class="dialog-content">
<p data-i18n="resetDialogText">Are you sure you want to reset all your progress? This cannot be undone.</p>
<div class="dialog-actions">
<button id="cancel-reset" class="btn" data-i18n="cancel">Cancel</button>
<button id="confirm-reset" class="btn btn-ghost" data-i18n="resetAll">Reset All</button>
</div>
</div>
</dialog>
<!-- Learning Paths Dialog -->
<dialog id="paths-dialog" class="dialog">
<div class="dialog-header">
<h3 data-i18n="learningPathsTitle">Learning Paths</h3>
<button id="paths-dialog-close" class="dialog-close" aria-label="Close">&times;</button>
</div>
<div class="dialog-content">
<p data-i18n="learningPathsDescription">
Choose a guided learning path to help you reach your goals. Each path includes a curated sequence of lessons.
</p>
<div id="paths-list" class="paths-list" role="list">
<!-- Path cards will be dynamically inserted here -->
</div>
</div>
</dialog>
<!-- Path Completion Celebration Dialog -->
<dialog id="path-completion-dialog" class="dialog celebration-dialog">
<div class="dialog-header">
<h3 data-i18n="pathCompletionTitle">🎉 Path Complete!</h3>
<button id="path-completion-dialog-close" class="dialog-close" aria-label="Close">&times;</button>
</div>
<div class="dialog-content">
<p class="celebration-message" data-i18n="pathCompletionMessage">Congratulations! You've completed this learning path.</p>
<div class="completion-stats">
<div class="stat-item">
<span class="stat-icon">📚</span>
<div class="stat-content">
<span class="stat-label" data-i18n="lessonsCompleted">Lessons Completed</span>
<span class="stat-value" id="completion-lessons-count">0</span>
</div>
</div>
<div class="stat-item">
<span class="stat-icon">⏱️</span>
<div class="stat-content">
<span class="stat-label" data-i18n="timeTaken">Time Taken</span>
<span class="stat-value" id="completion-time-taken">0 min</span>
</div>
</div>
</div>
<div class="next-path-suggestion" id="next-path-suggestion" style="display: none;">
<p class="suggestion-label" data-i18n="recommendedNextPath">Recommended next path:</p>
<div class="suggested-path-card">
<h4 id="suggested-path-title"></h4>
<p id="suggested-path-goal"></p>
<button id="start-suggested-path-btn" class="btn btn-primary" data-i18n="startThisPath">Start This Path</button>
</div>
</div>
<div class="dialog-actions">
<button id="view-all-paths-from-completion" class="btn" data-i18n="viewAllPaths">View All Paths</button>
<button id="close-completion-dialog" class="btn btn-ghost" data-i18n="continueLearning">Continue Learning</button>
</div>
</div>
</dialog>
</div>
<script type="module" src="app.js"></script>
</body>
</html>