feat(ui): WCAG text contrast improvements and modern course & lesson navigation button system
This commit is contained in:
parent
ec6fc53c81
commit
dd43e98748
3 changed files with 790 additions and 22 deletions
|
|
@ -7,6 +7,16 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function getRootPrefix() {
|
||||
const scripts = document.querySelectorAll('script[src*="course_sidebar.js"]');
|
||||
if (scripts.length > 0) {
|
||||
|
|
@ -104,7 +114,7 @@
|
|||
|
||||
if (!window.AUTH) {
|
||||
const authScript = document.createElement('script');
|
||||
authScript.src = `${rootPrefix}assets/app_auth.js?v=2.1.0`;
|
||||
authScript.src = `${rootPrefix}assets/app_auth.js?v=2.2.3`;
|
||||
authScript.onload = () => proceed();
|
||||
authScript.onerror = () => proceed();
|
||||
document.head.appendChild(authScript);
|
||||
|
|
@ -365,26 +375,425 @@
|
|||
document.body.appendChild(mobileBtn);
|
||||
}
|
||||
|
||||
// Workspace: Topbar "☰ Kapitel" Button einbinden
|
||||
if (context.isWorkspace) {
|
||||
// ==========================================================================
|
||||
// 🧭 KURS- & MODUL-NAVIGATIONSSYSTEM (Keine langweiligen Hyperlinks!)
|
||||
// ==========================================================================
|
||||
const ORDERED_COURSE_KEYS = [
|
||||
'python',
|
||||
'bash',
|
||||
'powershell',
|
||||
'active_directory',
|
||||
'git',
|
||||
'dns_records',
|
||||
'sql',
|
||||
'html_css',
|
||||
'javascript',
|
||||
'java',
|
||||
'csharp',
|
||||
'cpp',
|
||||
'go',
|
||||
'rust',
|
||||
'ihk_ap1',
|
||||
'ihk_ap2_fisi'
|
||||
];
|
||||
|
||||
const courseIdx = ORDERED_COURSE_KEYS.indexOf(context.courseId);
|
||||
const prevCourseKey = courseIdx > 0 ? ORDERED_COURSE_KEYS[courseIdx - 1] : null;
|
||||
const nextCourseKey = (courseIdx >= 0 && courseIdx < ORDERED_COURSE_KEYS.length - 1) ? ORDERED_COURSE_KEYS[courseIdx + 1] : null;
|
||||
const prevCourse = prevCourseKey ? manifest[prevCourseKey] : null;
|
||||
const nextCourse = nextCourseKey ? manifest[nextCourseKey] : null;
|
||||
|
||||
const allChapters = [];
|
||||
(currentCourse.tracks || []).forEach(tr => {
|
||||
(tr.chapters || []).forEach(ch => {
|
||||
allChapters.push({
|
||||
trackId: tr.id,
|
||||
folder: ch.folder,
|
||||
title: ch.title,
|
||||
taskFile: ch.taskFile,
|
||||
testFile: ch.testFile
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const currentChapIdx = allChapters.findIndex(ch => ch.folder === context.chapterFolder);
|
||||
const prevChapter = currentChapIdx > 0 ? allChapters[currentChapIdx - 1] : null;
|
||||
const nextChapter = (currentChapIdx >= 0 && currentChapIdx < allChapters.length - 1) ? allChapters[currentChapIdx + 1] : null;
|
||||
|
||||
function buildTheoryUrl(cid, tid, folder) {
|
||||
if (cid === 'python') {
|
||||
return `${rootPrefix}${tid || 'lehrpfad_1_grundlagen'}/${folder}/index.html`;
|
||||
}
|
||||
return `${rootPrefix}courses/${cid}/${folder}/index.html`;
|
||||
}
|
||||
|
||||
function buildFirstLessonTheoryUrl(cid, courseObj) {
|
||||
if (!courseObj || !courseObj.tracks || !courseObj.tracks.length) return `${rootPrefix}dashboard.html`;
|
||||
const t = courseObj.tracks[0];
|
||||
const f = t.chapters && t.chapters.length ? t.chapters[0].folder : '';
|
||||
if (!f) return `${rootPrefix}dashboard.html`;
|
||||
return buildTheoryUrl(cid, t.id, f);
|
||||
}
|
||||
|
||||
function buildWorkspaceUrl(cid, tid, folder) {
|
||||
return `${rootPrefix}workspace.html?course=${encodeURIComponent(cid)}&track=${encodeURIComponent(tid || '')}&chapter=${encodeURIComponent(folder)}`;
|
||||
}
|
||||
|
||||
function buildFirstLessonWorkspaceUrl(cid, courseObj) {
|
||||
if (!courseObj || !courseObj.tracks || !courseObj.tracks.length) return `${rootPrefix}workspace.html`;
|
||||
const t = courseObj.tracks[0];
|
||||
const f = t.chapters && t.chapters.length ? t.chapters[0].folder : '';
|
||||
return buildWorkspaceUrl(cid, t.id, f);
|
||||
}
|
||||
|
||||
// 1. THEORIE-SEITEN: Prominente Navigationsleiste am Ende der Lerneinheit
|
||||
function renderTheoryNavigator() {
|
||||
const mainEl = document.querySelector('main.container') || document.querySelector('main') || document.querySelector('.container');
|
||||
if (!mainEl) return;
|
||||
|
||||
const existingNav = document.getElementById('courseModuleNavigator');
|
||||
if (existingNav) existingNav.remove();
|
||||
|
||||
// Legacy-Buttons ausblenden
|
||||
const legacyActionDivs = mainEl.querySelectorAll('.card > div[style*="flex"]:last-child, main > div[style*="flex"]:last-child');
|
||||
legacyActionDivs.forEach(div => {
|
||||
if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) {
|
||||
div.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const nav = document.createElement('nav');
|
||||
nav.className = 'course-module-navigator';
|
||||
nav.id = 'courseModuleNavigator';
|
||||
nav.setAttribute('aria-label', 'Kurs- und Modulnavigation');
|
||||
|
||||
// Linker Button (Vorheriges Modul oder Vorheriger Kurs)
|
||||
let leftBtnHtml = '';
|
||||
if (prevChapter) {
|
||||
const prevUrl = buildTheoryUrl(context.courseId, prevChapter.trackId, prevChapter.folder);
|
||||
leftBtnHtml = `
|
||||
<a href="${prevUrl}" class="lesson-nav-btn lesson-nav-prev" title="Vorheriges Modul: ${escapeHtml(prevChapter.title)}">
|
||||
<span class="nav-arrow">◀</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">VORHERIGES MODUL</span>
|
||||
<span class="nav-title">${escapeHtml(prevChapter.title)}</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
} else if (prevCourse) {
|
||||
const prevCourseUrl = buildFirstLessonTheoryUrl(prevCourseKey, prevCourse);
|
||||
leftBtnHtml = `
|
||||
<a href="${prevCourseUrl}" class="lesson-nav-btn lesson-nav-course-prev" title="Vorheriger Kurs: ${escapeHtml(prevCourse.title)}">
|
||||
<span class="nav-arrow">⏮</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">VORHERIGER KURS</span>
|
||||
<span class="nav-title">${escapeHtml(prevCourse.icon || '📚')} ${escapeHtml(prevCourse.title)}</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
leftBtnHtml = `
|
||||
<a href="${rootPrefix}dashboard.html" class="lesson-nav-btn" style="opacity: 0.85;" title="Zurück zum Lern-Dashboard">
|
||||
<span class="nav-arrow">🏠</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">START</span>
|
||||
<span class="nav-title">Zum Dashboard</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
// Mittlere Buttons (Dashboard & Web-IDE)
|
||||
const ideUrl = buildWorkspaceUrl(context.courseId, currentTrackId, context.chapterFolder);
|
||||
const centerHtml = `
|
||||
<div class="lesson-nav-center">
|
||||
<a href="${rootPrefix}dashboard.html" class="lesson-nav-btn lesson-nav-dashboard" title="Zurück zur Übersicht aller Kurse">
|
||||
<span>🏠</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="${ideUrl}" class="lesson-nav-btn lesson-nav-ide" title="Aufgabe interaktiv in der Web-IDE lösen">
|
||||
<span>💻</span>
|
||||
<span>In Web-IDE lösen →</span>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Rechter Button (Nächstes Modul oder Nächster Kurs)
|
||||
let rightBtnHtml = '';
|
||||
if (nextChapter) {
|
||||
const nextUrl = buildTheoryUrl(context.courseId, nextChapter.trackId, nextChapter.folder);
|
||||
rightBtnHtml = `
|
||||
<a href="${nextUrl}" class="lesson-nav-btn lesson-nav-next" title="Nächstes Modul: ${escapeHtml(nextChapter.title)}">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">NÄCHSTES MODUL</span>
|
||||
<span class="nav-title">${escapeHtml(nextChapter.title)}</span>
|
||||
</span>
|
||||
<span class="nav-arrow">▶</span>
|
||||
</a>
|
||||
`;
|
||||
} else if (nextCourse) {
|
||||
const nextCourseUrl = buildFirstLessonTheoryUrl(nextCourseKey, nextCourse);
|
||||
rightBtnHtml = `
|
||||
<a href="${nextCourseUrl}" class="lesson-nav-btn lesson-nav-course-next" title="Nächster Kurs: ${escapeHtml(nextCourse.title)}">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">NÄCHSTER KURS</span>
|
||||
<span class="nav-title">${escapeHtml(nextCourse.icon || '🚀')} ${escapeHtml(nextCourse.title)}</span>
|
||||
</span>
|
||||
<span class="nav-arrow">⏭</span>
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
rightBtnHtml = `
|
||||
<a href="${rootPrefix}dashboard.html" class="lesson-nav-btn lesson-nav-course-next" title="Alle Kurse gemeistert!">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">ABSCHLUSS</span>
|
||||
<span class="nav-title">🎉 Kurs gemeistert!</span>
|
||||
</span>
|
||||
<span class="nav-arrow">🏆</span>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
nav.innerHTML = leftBtnHtml + centerHtml + rightBtnHtml;
|
||||
mainEl.appendChild(nav);
|
||||
}
|
||||
|
||||
// 2. WORKSPACE-PANE: Navigationsbuttons innerhalb des Theorie-Tabs der Web-IDE
|
||||
function renderWorkspaceTheoryInlineNavigator() {
|
||||
const theoriePane = document.getElementById('tab-content-theorie');
|
||||
if (!theoriePane) return;
|
||||
|
||||
const existingPaneNav = theoriePane.querySelector('.course-module-navigator');
|
||||
if (existingPaneNav) existingPaneNav.remove();
|
||||
|
||||
const legacyActionDivs = theoriePane.querySelectorAll('.card > div[style*="flex"]:last-child, div[style*="flex"]:last-child');
|
||||
legacyActionDivs.forEach(div => {
|
||||
if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) {
|
||||
div.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const paneNav = document.createElement('nav');
|
||||
paneNav.className = 'course-module-navigator';
|
||||
paneNav.style.marginTop = '28px';
|
||||
paneNav.setAttribute('aria-label', 'Modulnavigation');
|
||||
|
||||
// Left
|
||||
let leftHtml = '';
|
||||
if (prevChapter) {
|
||||
const prevWsUrl = buildWorkspaceUrl(context.courseId, prevChapter.trackId, prevChapter.folder);
|
||||
leftHtml = `
|
||||
<a href="${prevWsUrl}" class="lesson-nav-btn lesson-nav-prev" title="Vorheriges Modul: ${escapeHtml(prevChapter.title)}">
|
||||
<span class="nav-arrow">◀</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">VORHERIGES MODUL</span>
|
||||
<span class="nav-title">${escapeHtml(prevChapter.title)}</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
} else if (prevCourse) {
|
||||
const prevCourseWsUrl = buildFirstLessonWorkspaceUrl(prevCourseKey, prevCourse);
|
||||
leftHtml = `
|
||||
<a href="${prevCourseWsUrl}" class="lesson-nav-btn lesson-nav-course-prev" title="Vorheriger Kurs: ${escapeHtml(prevCourse.title)}">
|
||||
<span class="nav-arrow">⏮</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">VORHERIGER KURS</span>
|
||||
<span class="nav-title">${escapeHtml(prevCourse.icon || '📚')} ${escapeHtml(prevCourse.title)}</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
leftHtml = `
|
||||
<a href="${rootPrefix}dashboard.html" class="lesson-nav-btn" title="Zurück zum Lern-Dashboard">
|
||||
<span class="nav-arrow">🏠</span>
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">START</span>
|
||||
<span class="nav-title">Dashboard</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
// Center
|
||||
const theoryFullUrl = buildTheoryUrl(context.courseId, currentTrackId, context.chapterFolder);
|
||||
const centerHtml = `
|
||||
<div class="lesson-nav-center">
|
||||
<a href="${theoryFullUrl}" target="_blank" rel="noopener noreferrer" class="lesson-nav-btn lesson-nav-dashboard" title="Theorie in voller Seite lesen">
|
||||
<span>📖</span>
|
||||
<span>Vollbild-Theorie ↗</span>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Right
|
||||
let rightHtml = '';
|
||||
if (nextChapter) {
|
||||
const nextWsUrl = buildWorkspaceUrl(context.courseId, nextChapter.trackId, nextChapter.folder);
|
||||
rightHtml = `
|
||||
<a href="${nextWsUrl}" class="lesson-nav-btn lesson-nav-next" title="Nächstes Modul: ${escapeHtml(nextChapter.title)}">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">NÄCHSTES MODUL</span>
|
||||
<span class="nav-title">${escapeHtml(nextChapter.title)}</span>
|
||||
</span>
|
||||
<span class="nav-arrow">▶</span>
|
||||
</a>
|
||||
`;
|
||||
} else if (nextCourse) {
|
||||
const nextCourseWsUrl = buildFirstLessonWorkspaceUrl(nextCourseKey, nextCourse);
|
||||
rightHtml = `
|
||||
<a href="${nextCourseWsUrl}" class="lesson-nav-btn lesson-nav-course-next" title="Nächster Kurs: ${escapeHtml(nextCourse.title)}">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">NÄCHSTER KURS</span>
|
||||
<span class="nav-title">${escapeHtml(nextCourse.icon || '🚀')} ${escapeHtml(nextCourse.title)}</span>
|
||||
</span>
|
||||
<span class="nav-arrow">⏭</span>
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
rightHtml = `
|
||||
<a href="${rootPrefix}dashboard.html" class="lesson-nav-btn lesson-nav-course-next" title="Alle Kurse gemeistert!">
|
||||
<span class="nav-text">
|
||||
<span class="nav-subtitle">ABSCHLUSS</span>
|
||||
<span class="nav-title">🎉 Kurs gemeistert!</span>
|
||||
</span>
|
||||
<span class="nav-arrow">🏆</span>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
paneNav.innerHTML = leftHtml + centerHtml + rightHtml;
|
||||
theoriePane.appendChild(paneNav);
|
||||
}
|
||||
|
||||
// Global für dynamisches Nachladen in workspace.html bereitstellen
|
||||
window.renderWorkspaceTheoryNavigator = renderWorkspaceTheoryInlineNavigator;
|
||||
|
||||
// 3. WORKSPACE: Topbar-Steuerleiste verdrahten
|
||||
function renderWorkspaceNavigation() {
|
||||
const topbarTitle = document.querySelector('.workspace-title');
|
||||
if (topbarTitle && !document.getElementById('btn-toggle-chapter-drawer')) {
|
||||
const toggleBtn = document.createElement('button');
|
||||
if (!topbarTitle) return;
|
||||
|
||||
// 1. Drawer Button "☰ Kapitel"
|
||||
let toggleBtn = document.getElementById('btn-toggle-chapter-drawer');
|
||||
if (!toggleBtn) {
|
||||
toggleBtn = document.createElement('button');
|
||||
toggleBtn.type = 'button';
|
||||
toggleBtn.id = 'btn-toggle-chapter-drawer';
|
||||
toggleBtn.className = 'btn btn-secondary';
|
||||
toggleBtn.style.cssText = 'font-size: 0.8rem; padding: 4px 10px; margin-right: 10px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.08); color: #f8fafc; font-weight: 700; cursor: pointer; border-radius: 6px;';
|
||||
toggleBtn.className = 'workspace-topbar-btn';
|
||||
toggleBtn.innerHTML = '<span>☰</span> <span>Kapitel</span>';
|
||||
toggleBtn.title = 'Kapitel-Drawer aufklappen';
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
toggleBtn.title = 'Kapitel-Übersicht aufklappen';
|
||||
topbarTitle.insertBefore(toggleBtn, topbarTitle.firstChild);
|
||||
}
|
||||
toggleBtn.onclick = () => {
|
||||
if (sidebar.classList.contains('open')) {
|
||||
closeSidebar();
|
||||
} else {
|
||||
openSidebar();
|
||||
}
|
||||
});
|
||||
topbarTitle.insertBefore(toggleBtn, topbarTitle.firstChild);
|
||||
};
|
||||
|
||||
// 2. Dashboard Button
|
||||
let dashBtn = document.getElementById('btn-topbar-dashboard');
|
||||
if (!dashBtn) {
|
||||
dashBtn = document.createElement('a');
|
||||
dashBtn.href = `${rootPrefix}dashboard.html`;
|
||||
dashBtn.id = 'btn-topbar-dashboard';
|
||||
dashBtn.className = 'workspace-topbar-btn';
|
||||
dashBtn.innerHTML = '<span>🏠</span> <span>Dashboard</span>';
|
||||
dashBtn.title = 'Zurück zum Lern-Dashboard';
|
||||
toggleBtn.insertAdjacentElement('afterend', dashBtn);
|
||||
}
|
||||
|
||||
// 3. Navigation Cluster
|
||||
let navCluster = document.getElementById('workspace-nav-cluster');
|
||||
if (!navCluster) {
|
||||
navCluster = document.createElement('div');
|
||||
navCluster.id = 'workspace-nav-cluster';
|
||||
navCluster.className = 'workspace-nav-cluster';
|
||||
navCluster.innerHTML = `
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-prev-course" title="Vorheriger Kurs">⏮ Kurs</button>
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-prev-chapter" title="Vorheriges Modul">◀ Zurück</button>
|
||||
<span class="workspace-chapter-badge" id="workspace-chapter-badge">Modul - / -</span>
|
||||
<button type="button" class="workspace-topbar-btn primary" id="btn-topbar-next-chapter" title="Nächstes Modul">Weiter ▶</button>
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-next-course" title="Nächster Kurs">Kurs ⏭</button>
|
||||
`;
|
||||
dashBtn.insertAdjacentElement('afterend', navCluster);
|
||||
}
|
||||
|
||||
// Update Badge
|
||||
const badge = document.getElementById('workspace-chapter-badge');
|
||||
if (badge) {
|
||||
const modNumber = currentChapIdx >= 0 ? currentChapIdx + 1 : 1;
|
||||
badge.innerText = `MODUL ${modNumber} / ${allChapters.length || 16}`;
|
||||
}
|
||||
|
||||
const btnPrevCourse = document.getElementById('btn-topbar-prev-course');
|
||||
if (btnPrevCourse) {
|
||||
if (prevCourseKey && prevCourse) {
|
||||
btnPrevCourse.classList.remove('disabled');
|
||||
btnPrevCourse.disabled = false;
|
||||
btnPrevCourse.title = `Zum vorherigen Kurs: ${prevCourse.title}`;
|
||||
btnPrevCourse.onclick = () => {
|
||||
window.location.href = buildFirstLessonWorkspaceUrl(prevCourseKey, prevCourse);
|
||||
};
|
||||
} else {
|
||||
btnPrevCourse.classList.add('disabled');
|
||||
btnPrevCourse.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
const btnPrevChap = document.getElementById('btn-topbar-prev-chapter');
|
||||
if (btnPrevChap) {
|
||||
if (prevChapter) {
|
||||
btnPrevChap.classList.remove('disabled');
|
||||
btnPrevChap.disabled = false;
|
||||
btnPrevChap.title = `Vorheriges Modul: ${prevChapter.title}`;
|
||||
btnPrevChap.onclick = () => {
|
||||
window.location.href = buildWorkspaceUrl(context.courseId, prevChapter.trackId, prevChapter.folder);
|
||||
};
|
||||
} else {
|
||||
btnPrevChap.classList.add('disabled');
|
||||
btnPrevChap.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
const btnNextChap = document.getElementById('btn-topbar-next-chapter');
|
||||
if (btnNextChap) {
|
||||
if (nextChapter) {
|
||||
btnNextChap.classList.remove('disabled');
|
||||
btnNextChap.disabled = false;
|
||||
btnNextChap.title = `Nächstes Modul: ${nextChapter.title}`;
|
||||
btnNextChap.onclick = () => {
|
||||
window.location.href = buildWorkspaceUrl(context.courseId, nextChapter.trackId, nextChapter.folder);
|
||||
};
|
||||
} else {
|
||||
btnNextChap.classList.add('disabled');
|
||||
btnNextChap.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
const btnNextCourse = document.getElementById('btn-topbar-next-course');
|
||||
if (btnNextCourse) {
|
||||
if (nextCourseKey && nextCourse) {
|
||||
btnNextCourse.classList.remove('disabled');
|
||||
btnNextCourse.disabled = false;
|
||||
btnNextCourse.title = `Zum nächsten Kurs: ${nextCourse.title}`;
|
||||
btnNextCourse.onclick = () => {
|
||||
window.location.href = buildFirstLessonWorkspaceUrl(nextCourseKey, nextCourse);
|
||||
};
|
||||
} else {
|
||||
btnNextCourse.classList.add('disabled');
|
||||
btnNextCourse.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
renderWorkspaceTheoryInlineNavigator();
|
||||
}
|
||||
|
||||
if (context.isWorkspace) {
|
||||
renderWorkspaceNavigation();
|
||||
} else {
|
||||
renderTheoryNavigator();
|
||||
}
|
||||
|
||||
// Aktives Kapitel automatisch mittig in der Sidebar sichtbar scrollen
|
||||
|
|
|
|||
351
assets/style.css
351
assets/style.css
|
|
@ -37,8 +37,8 @@
|
|||
|
||||
/* Typography */
|
||||
--text-main: #0f172a;
|
||||
--text-body: #334155;
|
||||
--text-muted: #64748b;
|
||||
--text-body: #1e293b;
|
||||
--text-muted: #475569;
|
||||
--text-inverse: #f8fafc;
|
||||
|
||||
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
|
|
@ -77,6 +77,17 @@ body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0284c7;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: color 0.15s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
APP LAYOUT (FLEX CONTAINER)
|
||||
========================================================================== */
|
||||
|
|
@ -276,7 +287,7 @@ header h1 {
|
|||
|
||||
header p {
|
||||
font-size: 1.05rem;
|
||||
color: #94a3b8;
|
||||
color: #e2e8f0;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
|
|
@ -2540,3 +2551,337 @@ body.workspace-page.has-course-sidebar {
|
|||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
📊 HIGH-CONTRAST TABLES & CHEATSHEETS
|
||||
========================================================================== */
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.92rem;
|
||||
margin: 16px 0;
|
||||
text-align: left;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
|
||||
thead {
|
||||
background: var(--bg-subtle);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 12px 16px;
|
||||
font-weight: 800;
|
||||
color: var(--text-main);
|
||||
letter-spacing: 0.3px;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-body);
|
||||
line-height: 1.5;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: rgba(241, 245, 249, 0.6);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
🎯 CHECKLISTS & TASK ITEMS
|
||||
========================================================================== */
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 8px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
background: #f1f5f9;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.task-checkbox {
|
||||
margin-top: 4px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: var(--primary);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.task-label {
|
||||
font-size: 0.94rem;
|
||||
color: var(--text-main);
|
||||
line-height: 1.5;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
🧭 PREMIUM COURSE & LESSON NAVIGATOR COMPONENT
|
||||
========================================================================== */
|
||||
|
||||
.course-module-navigator {
|
||||
margin-top: 36px;
|
||||
margin-bottom: 20px;
|
||||
padding: 22px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.lesson-nav-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 18px;
|
||||
border-radius: 10px;
|
||||
font-weight: 700;
|
||||
text-decoration: none !important;
|
||||
font-size: 0.92rem;
|
||||
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
box-shadow: var(--shadow-sm);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.lesson-nav-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Vorheriger Schritt (Secondary Button Card) */
|
||||
.lesson-nav-prev {
|
||||
background: #ffffff;
|
||||
color: #0f172a !important;
|
||||
border: 1px solid #cbd5e1;
|
||||
}
|
||||
.lesson-nav-prev:hover {
|
||||
background: #f1f5f9;
|
||||
border-color: #94a3b8;
|
||||
box-shadow: var(--shadow-md);
|
||||
color: #0284c7 !important;
|
||||
}
|
||||
|
||||
/* Nächster Schritt (Primary Glowing Button Card) */
|
||||
.lesson-nav-next {
|
||||
background: linear-gradient(135deg, #0284c7 0%, #1d4ed8 100%);
|
||||
color: #ffffff !important;
|
||||
border: 1px solid #0284c7;
|
||||
box-shadow: 0 4px 14px rgba(2, 132, 199, 0.35);
|
||||
}
|
||||
.lesson-nav-next:hover {
|
||||
background: linear-gradient(135deg, #0369a1 0%, #1e40af 100%);
|
||||
box-shadow: 0 6px 20px rgba(2, 132, 199, 0.5);
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Kurs-Sprung Vorheriger Kurs */
|
||||
.lesson-nav-course-prev {
|
||||
background: #f8fafc;
|
||||
color: #334155 !important;
|
||||
border: 1px solid #cbd5e1;
|
||||
}
|
||||
.lesson-nav-course-prev:hover {
|
||||
background: #e2e8f0;
|
||||
border-color: #94a3b8;
|
||||
color: #0f172a !important;
|
||||
}
|
||||
|
||||
/* Kurs-Sprung Nächster Kurs (Emerald Accent) */
|
||||
.lesson-nav-course-next {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: #ffffff !important;
|
||||
border: 1px solid #10b981;
|
||||
box-shadow: 0 4px 14px rgba(16, 185, 129, 0.35);
|
||||
}
|
||||
.lesson-nav-course-next:hover {
|
||||
background: linear-gradient(135deg, #059669 0%, #047857 100%);
|
||||
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.5);
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.lesson-nav-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.lesson-nav-dashboard {
|
||||
background: #f1f5f9;
|
||||
color: #334155 !important;
|
||||
border: 1px solid #cbd5e1;
|
||||
padding: 10px 16px;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.lesson-nav-dashboard:hover {
|
||||
background: #e2e8f0;
|
||||
color: #0f172a !important;
|
||||
}
|
||||
|
||||
.lesson-nav-ide {
|
||||
background: #10b981;
|
||||
color: #ffffff !important;
|
||||
border: 1px solid #059669;
|
||||
padding: 10px 18px;
|
||||
font-size: 0.88rem;
|
||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
.lesson-nav-ide:hover {
|
||||
background: #059669;
|
||||
box-shadow: 0 6px 18px rgba(16, 185, 129, 0.45);
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
}
|
||||
.nav-subtitle,
|
||||
.nav-sub {
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.nav-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
max-width: 210px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.lesson-nav-next .nav-text,
|
||||
.lesson-nav-course-next .nav-text {
|
||||
text-align: right;
|
||||
}
|
||||
.nav-arrow {
|
||||
font-size: 1.15rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
💻 WORKSPACE TOPBAR & NAVIGATION BUTTON CLUSTER
|
||||
========================================================================== */
|
||||
|
||||
.workspace-nav-cluster {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 11px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
color: #f1f5f9 !important;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
cursor: pointer;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.15s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.workspace-topbar-btn.primary {
|
||||
background: #0284c7;
|
||||
border-color: #38bdf8;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn.primary:hover {
|
||||
background: #0369a1;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn.success {
|
||||
background: #10b981;
|
||||
border-color: #34d399;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn.success:hover {
|
||||
background: #059669;
|
||||
}
|
||||
|
||||
.workspace-topbar-btn.disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.workspace-chapter-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
background: rgba(56, 189, 248, 0.18);
|
||||
border: 1px solid rgba(56, 189, 248, 0.35);
|
||||
color: #38bdf8;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.course-module-navigator {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.lesson-nav-btn {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lesson-nav-center {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
.lesson-nav-center .lesson-nav-btn {
|
||||
flex: 1;
|
||||
}
|
||||
.nav-title {
|
||||
max-width: 100%;
|
||||
}
|
||||
.workspace-nav-cluster #btn-topbar-prev-course,
|
||||
.workspace-nav-cluster #btn-topbar-next-course {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,10 +223,21 @@
|
|||
<!-- Topbar -->
|
||||
<div class="workspace-topbar">
|
||||
<div class="workspace-title">
|
||||
<a href="dashboard.html" style="color: #94a3b8; text-decoration: none; font-size: 0.88rem;">🏠 Dashboard</a>
|
||||
<span style="color: #64748b;">/</span>
|
||||
<span id="workspace-chapter-name">Lerneinheit...</span>
|
||||
<span id="editor-save-indicator" style="font-size: 0.75rem; color: #10b981; opacity: 0.5; margin-left: 10px;">💾 Gespeichert</span>
|
||||
<button type="button" id="btn-toggle-chapter-drawer" class="workspace-topbar-btn" title="Kapitel-Übersicht öffnen">
|
||||
<span>☰</span> <span>Kapitel</span>
|
||||
</button>
|
||||
<a href="dashboard.html" id="btn-topbar-dashboard" class="workspace-topbar-btn" title="Zurück zum Lern-Dashboard">
|
||||
<span>🏠</span> <span>Dashboard</span>
|
||||
</a>
|
||||
<div class="workspace-nav-cluster" id="workspace-nav-cluster">
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-prev-course" title="Vorheriger Kurs">⏮ Kurs</button>
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-prev-chapter" title="Vorheriges Modul">◀ Zurück</button>
|
||||
<span class="workspace-chapter-badge" id="workspace-chapter-badge">Modul - / -</span>
|
||||
<button type="button" class="workspace-topbar-btn primary" id="btn-topbar-next-chapter" title="Nächstes Modul">Weiter ▶</button>
|
||||
<button type="button" class="workspace-topbar-btn" id="btn-topbar-next-course" title="Nächster Kurs">Kurs ⏭</button>
|
||||
</div>
|
||||
<span id="workspace-chapter-name" style="font-size: 0.84rem; font-weight: 700; color: #f8fafc; margin-left: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 260px;">Lerneinheit...</span>
|
||||
<span id="editor-save-indicator" style="font-size: 0.75rem; color: #10b981; opacity: 0.7; margin-left: 8px;">💾 Gespeichert</span>
|
||||
</div>
|
||||
|
||||
<div class="workspace-actions">
|
||||
|
|
@ -524,6 +535,9 @@
|
|||
const doc = parser.parseFromString(htmlDoc, "text/html");
|
||||
const mainContent = doc.querySelector("main.container") || doc.body;
|
||||
document.getElementById("tab-content-theorie").innerHTML = mainContent.innerHTML;
|
||||
if (window.renderWorkspaceTheoryNavigator) {
|
||||
window.renderWorkspaceTheoryNavigator();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Subgoals (Exakte Regex ohne Duplikate)
|
||||
|
|
|
|||
Loading…
Reference in a new issue