mirror of
https://github.com/blshaer/python-by-example.git
synced 2026-03-27 23:29:25 +01:00
Update core HTML structure, styling, and JavaScript logic.
This commit is contained in:
parent
cad75300d3
commit
02984c824d
127
index.html
127
index.html
|
|
@ -1,127 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Python By Example | Interactive Learning Docs</title>
|
|
||||||
|
|
||||||
<!-- SEO Meta Tags -->
|
|
||||||
<meta name="description"
|
|
||||||
content="A premium, interactive documentation hub for mastering Python by example. Run code live in your browser.">
|
|
||||||
<meta name="keywords" content="Python, Tutorial, Learn Python, Interactive Python, Pyodide, Coding, Programming">
|
|
||||||
<meta name="author" content="blshaer">
|
|
||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="og:url" content="https://pythonbyexample.github.io/">
|
|
||||||
<meta property="og:title" content="Python By Example | Interactive Learning Docs">
|
|
||||||
<meta property="og:description"
|
|
||||||
content="Master Python with real-world examples and an interactive browser-only playground.">
|
|
||||||
<meta property="og:image"
|
|
||||||
content="https://cdn.dribbble.com/userupload/46242920/file/bdbef935da688edfd85fcfeca75754a0.png?resize=1200x675&vertical=center">
|
|
||||||
|
|
||||||
<!-- Twitter -->
|
|
||||||
<meta property="twitter:card" content="summary_large_image">
|
|
||||||
<meta property="twitter:url" content="https://pythonbyexample.github.io/">
|
|
||||||
<meta property="twitter:title" content="Python By Example | Interactive Learning Docs">
|
|
||||||
<meta property="twitter:description"
|
|
||||||
content="Master Python with real-world examples and an interactive browser-only playground.">
|
|
||||||
<meta property="twitter:image"
|
|
||||||
content="https://cdn.dribbble.com/userupload/46242920/file/bdbef935da688edfd85fcfeca75754a0.png?resize=1200x675&vertical=center">
|
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap"
|
|
||||||
rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
||||||
<!-- Monaco Editor -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js"></script>
|
|
||||||
<!-- Pyodide Core -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="readme-theme">
|
|
||||||
<div id="playground-modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<div class="file-info">
|
|
||||||
<i class="fab fa-python"></i>
|
|
||||||
<span id="lesson-title">Playground.py</span>
|
|
||||||
</div>
|
|
||||||
<div class="header-actions">
|
|
||||||
<button id="edit-btn" class="btn-ghost" title="Enable editing"><i class="fas fa-edit"></i>
|
|
||||||
Edit</button>
|
|
||||||
<button id="reset-btn" class="btn-ghost" title="Reset to original code"><i class="fas fa-undo"></i>
|
|
||||||
Reset</button>
|
|
||||||
<button id="run-btn" class="btn-run" disabled><i class="fas fa-play"></i> Run Code</button>
|
|
||||||
<button class="close-modal"><i class="fas fa-times"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="playground-body">
|
|
||||||
<div class="editor-pane">
|
|
||||||
<div id="monaco-editor-container" style="width: 100%; height: 100%;"></div>
|
|
||||||
</div>
|
|
||||||
<div class="output-pane">
|
|
||||||
<div class="output-header">Terminal Output</div>
|
|
||||||
<pre id="output-area">Loading Python environment... (Ready in ~10s)</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<nav class="top-nav">
|
|
||||||
<div class="nav-container">
|
|
||||||
<div class="logo">
|
|
||||||
<i class="fab fa-python"></i>
|
|
||||||
<span>Python<span>By Example</span></span>
|
|
||||||
</div>
|
|
||||||
<div class="search-box">
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
<input type="text" placeholder="Search documentation...">
|
|
||||||
</div>
|
|
||||||
<div class="ext-links">
|
|
||||||
<a href="https://github.com/blshaer/python-by-example" target="_blank" title="GitHub Repository"><i
|
|
||||||
class="fab fa-github"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="app-layout">
|
|
||||||
<aside class="sidebar">
|
|
||||||
<div class="sidebar-header">Table of Contents</div>
|
|
||||||
<nav id="toc" class="toc-nav">
|
|
||||||
<!-- TOC links will be injected here -->
|
|
||||||
<div class="skeleton-line"></div>
|
|
||||||
<div class="skeleton-line"></div>
|
|
||||||
<div class="skeleton-line"></div>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<main class="content-area">
|
|
||||||
<header class="readme-header">
|
|
||||||
<h1>Python Mastery <span class="version">v1.0.0</span></h1>
|
|
||||||
|
|
||||||
<p class="lead">Complete roadmap for mastering Python, structured for clarity and professional growth.
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section id="documentation-content">
|
|
||||||
<!-- Documentation modules will be injected here -->
|
|
||||||
<div class="skeleton-section"></div>
|
|
||||||
<div class="skeleton-section"></div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="docs-footer">
|
|
||||||
<div class="footer-inner">
|
|
||||||
<p>Made with ❤️ for the Python Community.</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
353
script.js
353
script.js
|
|
@ -1,353 +0,0 @@
|
||||||
const documentation = [
|
|
||||||
{
|
|
||||||
id: "01_basics",
|
|
||||||
title: "01. Python Basics",
|
|
||||||
description: "Fundamental syntax and data types.",
|
|
||||||
icon: "fas fa-seedling",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Print Function", file: "01_basics/01_print.py" },
|
|
||||||
{ title: "Comments", file: "01_basics/02_comments.py" },
|
|
||||||
{ title: "Variables", file: "01_basics/03_variables.py" },
|
|
||||||
{ title: "Data Types", file: "01_basics/04_data_types.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "02_control_flow",
|
|
||||||
title: "02. Control Flow",
|
|
||||||
description: "Conditional logic and selection.",
|
|
||||||
icon: "fas fa-code-branch",
|
|
||||||
lessons: [
|
|
||||||
{ title: "If-Else", file: "02_control_flow/01_if_else.py" },
|
|
||||||
{ title: "Elif", file: "02_control_flow/02_elif.py" },
|
|
||||||
{ title: "Match Case", file: "02_control_flow/03_match_case.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "03_loops",
|
|
||||||
title: "03. Loops & Iterations",
|
|
||||||
description: "Repetitive task automation.",
|
|
||||||
icon: "fas fa-sync",
|
|
||||||
lessons: [
|
|
||||||
{ title: "For Loops", file: "03_loops/01_for_loop.py" },
|
|
||||||
{ title: "While Loops", file: "03_loops/02_while_loop.py" },
|
|
||||||
{ title: "Break & Continue", file: "03_loops/03_break_continue.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "04_data_structures",
|
|
||||||
title: "04. Data Structures",
|
|
||||||
description: "Collections and data management.",
|
|
||||||
icon: "fas fa-database",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Lists", file: "04_data_structures/01_lists.py" },
|
|
||||||
{ title: "Tuples", file: "04_data_structures/02_tuples.py" },
|
|
||||||
{ title: "Sets", file: "04_data_structures/03_sets.py" },
|
|
||||||
{ title: "Dictionaries", file: "04_data_structures/04_dictionaries.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "05_functions",
|
|
||||||
title: "05. Functions",
|
|
||||||
description: "Modular and reusable blocks.",
|
|
||||||
icon: "fas fa-cube",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Function Basics", file: "05_functions/01_function_basics.py" },
|
|
||||||
{ title: "Arguments", file: "05_functions/02_arguments.py" },
|
|
||||||
{ title: "Return Values", file: "05_functions/03_return_values.py" },
|
|
||||||
{ title: "Lambda Functions", file: "05_functions/04_lambda_functions.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "06_modules_packages",
|
|
||||||
title: "06. Modules & Packages",
|
|
||||||
description: "Code organization and imports.",
|
|
||||||
icon: "fas fa-box-open",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Imports", file: "06_modules_packages/01_imports.py" },
|
|
||||||
{ title: "Custom Modules", file: "06_modules_packages/02_custom_modules.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "07_error_handling",
|
|
||||||
title: "07. Error Handling",
|
|
||||||
description: "Robustness and exception safety.",
|
|
||||||
icon: "fas fa-exclamation-triangle",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Try-Except", file: "07_error_handling/01_try_except.py" },
|
|
||||||
{ title: "Custom Exceptions", file: "07_error_handling/02_custom_exceptions.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "08_oop",
|
|
||||||
title: "08. Object Oriented Programming",
|
|
||||||
description: "Classes, objects, and inheritance.",
|
|
||||||
icon: "fas fa-project-diagram",
|
|
||||||
lessons: [
|
|
||||||
{ title: "Classes & Objects", file: "08_oop/01_classes_objects.py" },
|
|
||||||
{ title: "Init Methods", file: "08_oop/02_init_methods.py" },
|
|
||||||
{ title: "Inheritance", file: "08_oop/03_inheritance.py" },
|
|
||||||
{ title: "Polymorphism", file: "08_oop/04_polymorphism.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "09_advanced_python",
|
|
||||||
title: "09. Advanced Python",
|
|
||||||
description: "Generators, decorators, and more.",
|
|
||||||
icon: "fas fa-bolt",
|
|
||||||
lessons: [
|
|
||||||
{ title: "List Comprehensions", file: "09_advanced_python/01_list_comprehensions.py" },
|
|
||||||
{ title: "Generators", file: "09_advanced_python/02_generators.py" },
|
|
||||||
{ title: "Decorators", file: "09_advanced_python/03_decorators.py" },
|
|
||||||
{ title: "Context Managers", file: "09_advanced_python/04_context_managers.py" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "10_best_practices",
|
|
||||||
title: "10. Best Practices",
|
|
||||||
description: "Clean code and professional PEP 8.",
|
|
||||||
icon: "fas fa-check-double",
|
|
||||||
lessons: [
|
|
||||||
{ title: "PEP 8", file: "10_best_practices/01_pep8.py" },
|
|
||||||
{ title: "Type Hinting", file: "10_best_practices/02_type_hinting.py" },
|
|
||||||
{ title: "Virtual Envs", file: "10_best_practices/03_virtual_envs.py" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
let pyodideInstance = null;
|
|
||||||
let editor = null;
|
|
||||||
let originalCode = "";
|
|
||||||
|
|
||||||
// Initialize Monaco Editor
|
|
||||||
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' } });
|
|
||||||
require(['vs/editor/editor.main'], function () {
|
|
||||||
editor = monaco.editor.create(document.getElementById('monaco-editor-container'), {
|
|
||||||
value: "# Select a lesson to begin...",
|
|
||||||
language: 'python',
|
|
||||||
theme: 'vs-dark',
|
|
||||||
automaticLayout: true,
|
|
||||||
readOnly: true,
|
|
||||||
fontSize: 14,
|
|
||||||
minimap: { enabled: false },
|
|
||||||
lineNumbers: 'on',
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
padding: { top: 20 }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
async function initPyodide() {
|
|
||||||
const outputArea = document.getElementById('output-area');
|
|
||||||
try {
|
|
||||||
pyodideInstance = await loadPyodide();
|
|
||||||
outputArea.textContent = ">>> Python 3.10 (Pyodide) Ready.\n";
|
|
||||||
document.getElementById('run-btn').disabled = false;
|
|
||||||
} catch (err) {
|
|
||||||
outputArea.textContent = "Error loading Pyodide: " + err.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runPython(code) {
|
|
||||||
const outputArea = document.getElementById('output-area');
|
|
||||||
|
|
||||||
if (!pyodideInstance) {
|
|
||||||
outputArea.textContent = ">>> Error: Python environment not yet initialized. Please wait.\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputArea.textContent = "Running...\n";
|
|
||||||
|
|
||||||
try {
|
|
||||||
pyodideInstance.setStdout({
|
|
||||||
batched: (text) => {
|
|
||||||
outputArea.textContent += text + "\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await pyodideInstance.runPythonAsync(code);
|
|
||||||
outputArea.textContent += "\n[Execution Complete]";
|
|
||||||
} catch (err) {
|
|
||||||
outputArea.textContent += "\nTraceback (most recent call last):\n" + err.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
initPyodide();
|
|
||||||
|
|
||||||
const content = document.getElementById('documentation-content');
|
|
||||||
const toc = document.getElementById('toc');
|
|
||||||
const modal = document.getElementById('playground-modal');
|
|
||||||
const lessonTitle = document.getElementById('lesson-title');
|
|
||||||
const resetBtn = document.getElementById('reset-btn');
|
|
||||||
const editBtn = document.getElementById('edit-btn');
|
|
||||||
const runBtn = document.getElementById('run-btn');
|
|
||||||
const closeModal = document.querySelector('.close-modal');
|
|
||||||
|
|
||||||
content.innerHTML = '';
|
|
||||||
toc.innerHTML = '';
|
|
||||||
|
|
||||||
// Render Documentation
|
|
||||||
documentation.forEach((module) => {
|
|
||||||
const section = document.createElement('section');
|
|
||||||
section.id = module.id;
|
|
||||||
section.className = 'doc-section';
|
|
||||||
|
|
||||||
section.innerHTML = `
|
|
||||||
<h2><i class="${module.icon || 'fas fa-book'}" style="margin-right: 12px; font-size: 1.2rem; color: var(--accent);"></i>${module.title}</h2>
|
|
||||||
<p>${module.description}</p>
|
|
||||||
<div class="module-card">
|
|
||||||
<ul class="lesson-list" style="list-style: none; padding-left: 0;">
|
|
||||||
${module.lessons.map(lesson => `
|
|
||||||
<li class="lesson-item" data-file="${lesson.file}" data-title="${lesson.title}"
|
|
||||||
style="padding: 12px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; justify-content: space-between; cursor: pointer; transition: 0.2s; border-radius: 6px;">
|
|
||||||
<div style="display: flex; align-items: center; gap: 12px;">
|
|
||||||
<i class="fab fa-python" style="color: var(--accent);"></i>
|
|
||||||
<span>${lesson.title}</span>
|
|
||||||
</div>
|
|
||||||
<span style="font-size: 0.8rem; color: var(--text-secondary);">Run Live <i class="fas fa-play" style="font-size: 0.6rem;"></i></span>
|
|
||||||
</li>
|
|
||||||
`).join('')}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
content.appendChild(section);
|
|
||||||
|
|
||||||
const tocLink = document.createElement('a');
|
|
||||||
tocLink.href = `#${module.id}`;
|
|
||||||
tocLink.className = 'toc-link';
|
|
||||||
tocLink.textContent = module.title;
|
|
||||||
toc.appendChild(tocLink);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle Lesson Clicks
|
|
||||||
document.addEventListener('click', async (e) => {
|
|
||||||
const item = e.target.closest('.lesson-item');
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
const file = item.getAttribute('data-file');
|
|
||||||
const title = item.getAttribute('data-title');
|
|
||||||
lessonTitle.textContent = `${title}.py`;
|
|
||||||
modal.classList.add('active');
|
|
||||||
|
|
||||||
// Monaco needs layout() called when container becomes visible to display cursor/suggestions correctly
|
|
||||||
setTimeout(() => {
|
|
||||||
if (editor) {
|
|
||||||
editor.layout();
|
|
||||||
editor.focus();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// Reset edit mode
|
|
||||||
editBtn.classList.remove('active');
|
|
||||||
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
|
|
||||||
if (editor) editor.updateOptions({ readOnly: true });
|
|
||||||
|
|
||||||
if (editor) editor.setValue("# Loading source code...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(file);
|
|
||||||
if (!response.ok) throw new Error("File not found");
|
|
||||||
const code = await response.text();
|
|
||||||
originalCode = code;
|
|
||||||
if (editor) {
|
|
||||||
editor.setValue(code);
|
|
||||||
// Force suggestions/intellisense refresh
|
|
||||||
monaco.editor.setModelLanguage(editor.getModel(), 'python');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const fallback = `# Error loading file: ${file}\n# serving from github.io might require valid paths.\n\nprint("Hello from Python By Example!")\nname = "Developer"\nprint(f"Happy coding, {name}!")`;
|
|
||||||
originalCode = fallback;
|
|
||||||
if (editor) editor.setValue(fallback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Edit Toggle
|
|
||||||
editBtn.addEventListener('click', () => {
|
|
||||||
if (!editor) return;
|
|
||||||
const isEditing = editBtn.classList.toggle('active');
|
|
||||||
editor.updateOptions({ readOnly: !isEditing });
|
|
||||||
|
|
||||||
if (isEditing) {
|
|
||||||
editBtn.innerHTML = '<i class="fas fa-check"></i> Editing...';
|
|
||||||
editor.focus();
|
|
||||||
document.getElementById('output-area').textContent = ">>> Editing mode enabled. You can now modify the code and see suggestions.\n";
|
|
||||||
} else {
|
|
||||||
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
|
|
||||||
document.getElementById('output-area').textContent = ">>> Editing mode disabled.\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset Logic
|
|
||||||
resetBtn.addEventListener('click', () => {
|
|
||||||
if (!editor) return;
|
|
||||||
editor.setValue(originalCode);
|
|
||||||
editBtn.classList.remove('active');
|
|
||||||
editor.updateOptions({ readOnly: true });
|
|
||||||
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
|
|
||||||
document.getElementById('output-area').textContent = ">>> Code reset to original state.\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run Logic
|
|
||||||
runBtn.addEventListener('click', () => {
|
|
||||||
if (editor) runPython(editor.getValue());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Modal Controls
|
|
||||||
closeModal.addEventListener('click', () => modal.classList.remove('active'));
|
|
||||||
modal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === modal) modal.classList.remove('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search Functionality (Improved)
|
|
||||||
const searchInput = document.querySelector('.search-box input');
|
|
||||||
searchInput.addEventListener('input', (e) => {
|
|
||||||
const query = e.target.value.toLowerCase().trim();
|
|
||||||
|
|
||||||
document.querySelectorAll('.doc-section').forEach(section => {
|
|
||||||
const heading = section.querySelector('h2').textContent.toLowerCase();
|
|
||||||
const desc = section.querySelector('p').textContent.toLowerCase();
|
|
||||||
const lessonItems = section.querySelectorAll('.lesson-item');
|
|
||||||
|
|
||||||
let isSectionVisible = query === "" || heading.includes(query) || desc.includes(query);
|
|
||||||
let matchingLessonsCount = 0;
|
|
||||||
|
|
||||||
lessonItems.forEach(item => {
|
|
||||||
const lessonText = item.textContent.toLowerCase();
|
|
||||||
const isLessonMatch = query === "" || lessonText.includes(query);
|
|
||||||
|
|
||||||
// Show lesson if it matches or if the section title matches
|
|
||||||
const shouldShowLesson = isSectionVisible || isLessonMatch;
|
|
||||||
item.style.setProperty('display', shouldShowLesson ? 'flex' : 'none', 'important');
|
|
||||||
|
|
||||||
if (isLessonMatch) matchingLessonsCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show section if title matches or if any lesson inside matches
|
|
||||||
section.style.display = (isSectionVisible || matchingLessonsCount > 0) ? 'block' : 'none';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sync TOC
|
|
||||||
document.querySelectorAll('.toc-link').forEach(link => {
|
|
||||||
const targetId = link.getAttribute('href').substring(1);
|
|
||||||
const targetSection = document.getElementById(targetId);
|
|
||||||
if (targetSection) {
|
|
||||||
link.style.display = targetSection.style.display === 'none' ? 'none' : 'block';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Scroll Spy
|
|
||||||
window.addEventListener('scroll', () => {
|
|
||||||
let current = '';
|
|
||||||
document.querySelectorAll('.doc-section').forEach(section => {
|
|
||||||
if (section.style.display !== 'none' && window.pageYOffset >= (section.offsetTop - 150)) {
|
|
||||||
current = section.getAttribute('id');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.querySelectorAll('.toc-link').forEach(link => {
|
|
||||||
link.classList.remove('active');
|
|
||||||
if (link.style.display !== 'none' && link.getAttribute('href').substring(1) === current) {
|
|
||||||
link.classList.add('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
497
style.css
497
style.css
|
|
@ -1,497 +0,0 @@
|
||||||
:root {
|
|
||||||
--bg-main: #0d1117;
|
|
||||||
--bg-side: #010409;
|
|
||||||
--border-color: #30363d;
|
|
||||||
--text-primary: #c9d1d9;
|
|
||||||
--text-secondary: #8b949e;
|
|
||||||
--accent: #58a6ff;
|
|
||||||
--accent-bg: rgba(56, 139, 253, 0.15);
|
|
||||||
--code-bg: #161b22;
|
|
||||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
||||||
--font-mono: 'Fira Code', ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: var(--bg-main);
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
line-height: 1.6;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navigation */
|
|
||||||
.top-nav {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
background: var(--bg-side);
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
padding: 12px 0;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-container {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo span span {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
position: relative;
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box i {
|
|
||||||
position: absolute;
|
|
||||||
left: 12px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box input {
|
|
||||||
width: 100%;
|
|
||||||
background: var(--code-bg);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 8px 12px 8px 36px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
outline: none;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box input:focus {
|
|
||||||
border-color: var(--accent);
|
|
||||||
box-shadow: 0 0 0 3px var(--accent-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Layout */
|
|
||||||
.app-layout {
|
|
||||||
display: flex;
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 40px auto;
|
|
||||||
padding: 0 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 280px;
|
|
||||||
position: sticky;
|
|
||||||
top: 100px;
|
|
||||||
height: calc(100vh - 120px);
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-header {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-nav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-link {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.95rem;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-link:hover,
|
|
||||||
.toc-link.active {
|
|
||||||
color: var(--text-primary);
|
|
||||||
background: var(--code-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-link.active {
|
|
||||||
border-left: 2px solid var(--accent);
|
|
||||||
border-radius: 0 6px 6px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-area {
|
|
||||||
flex: 1;
|
|
||||||
max-width: 900px;
|
|
||||||
margin-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header & Typography */
|
|
||||||
.readme-header {
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
padding-bottom: 32px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.readme-header h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-weight: 400;
|
|
||||||
background: var(--code-bg);
|
|
||||||
padding: 4px 10px;
|
|
||||||
border-radius: 40px;
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lead {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badges {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 40px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-blue {
|
|
||||||
background: #053b6d;
|
|
||||||
color: #58a6ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-green {
|
|
||||||
background: #1b4721;
|
|
||||||
color: #7ee787;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-purple {
|
|
||||||
background: #4c2889;
|
|
||||||
color: #d2a8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sections */
|
|
||||||
.doc-section {
|
|
||||||
margin-bottom: 60px;
|
|
||||||
scroll-margin-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-section h2 {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
padding-bottom: 12px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-section p {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.module-card {
|
|
||||||
background: var(--code-bg);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 24px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.module-card:hover {
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.module-meta {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 20px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Skeleton */
|
|
||||||
.skeleton-line {
|
|
||||||
height: 16px;
|
|
||||||
background: var(--code-bg);
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
animation: pulse 1.5s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skeleton-section {
|
|
||||||
height: 200px;
|
|
||||||
background: var(--code-bg);
|
|
||||||
border-radius: 12px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
animation: pulse 1.5s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modal & Playground */
|
|
||||||
.modal {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 2000;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.85);
|
|
||||||
backdrop-filter: blur(8px);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal.active {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
background: var(--bg-side);
|
|
||||||
width: 90%;
|
|
||||||
max-width: 1200px;
|
|
||||||
height: 85vh;
|
|
||||||
border-radius: 16px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
background: var(--bg-main);
|
|
||||||
padding: 16px 24px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info i {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-run {
|
|
||||||
background: var(--accent);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 20px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-run:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-run:disabled {
|
|
||||||
background: var(--text-secondary);
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-ghost {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-ghost:hover {
|
|
||||||
color: var(--text-primary);
|
|
||||||
background: var(--code-bg);
|
|
||||||
border-color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-ghost.active {
|
|
||||||
color: var(--accent);
|
|
||||||
border-color: var(--accent);
|
|
||||||
background: var(--accent-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-modal {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 1.25rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Monaco Editor Container */
|
|
||||||
#monaco-editor-container {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-body {
|
|
||||||
flex: 1;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 400px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-pane {
|
|
||||||
position: relative;
|
|
||||||
background: #1e1e1e;
|
|
||||||
overflow: hidden;
|
|
||||||
/* Monaco handles scroll */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Individual Lesson Hover */
|
|
||||||
.lesson-item {
|
|
||||||
background: transparent;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lesson-item:hover {
|
|
||||||
background: var(--accent-bg) !important;
|
|
||||||
padding-left: 16px !important;
|
|
||||||
/* Slight organic slide effect */
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-pane.edit-active {
|
|
||||||
outline: 2px solid var(--accent);
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.output-pane {
|
|
||||||
background: #010409;
|
|
||||||
border-left: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.output-header {
|
|
||||||
background: var(--bg-main);
|
|
||||||
padding: 8px 16px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#output-area {
|
|
||||||
flex: 1;
|
|
||||||
padding: 16px;
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #a5d6ff;
|
|
||||||
overflow-y: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar Style */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: var(--bg-side);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--border-color);
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
.docs-footer {
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
padding: 40px 0;
|
|
||||||
margin-top: 80px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
|
||||||
.sidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-area {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-body {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: 1fr 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.output-pane {
|
|
||||||
border-left: none;
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user