feat: add CodeMirror syntax highlighting to section page code blocks
- Use CodeMirror in read-only mode for code examples - Auto-detect CSS vs HTML based on content - Clean up views when navigating between sections - Add transparent background to blend with code-block container 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
72
src/app.js
72
src/app.js
@@ -6,6 +6,13 @@ import { initI18n, t, getLanguage, setLanguage, applyTranslations } from "./i18n
|
|||||||
import { parseHash, updateHash, replaceHash, getShareableUrl, RouteType, navigateTo } from "./helpers/router.js";
|
import { parseHash, updateHash, replaceHash, getShareableUrl, RouteType, navigateTo } from "./helpers/router.js";
|
||||||
import { sections, getSection, getModuleSection, getModulesBySection } from "./config/sections.js";
|
import { sections, getSection, getModuleSection, getModulesBySection } from "./config/sections.js";
|
||||||
|
|
||||||
|
// CodeMirror imports for syntax highlighting
|
||||||
|
import { EditorState } from "@codemirror/state";
|
||||||
|
import { EditorView } from "@codemirror/view";
|
||||||
|
import { oneDark } from "@codemirror/theme-one-dark";
|
||||||
|
import { html } from "@codemirror/lang-html";
|
||||||
|
import { css } from "@codemirror/lang-css";
|
||||||
|
|
||||||
// Simplified state - LessonEngine now manages lesson state and progress
|
// Simplified state - LessonEngine now manages lesson state and progress
|
||||||
const state = {
|
const state = {
|
||||||
userSettings: {
|
userSettings: {
|
||||||
@@ -16,6 +23,69 @@ const state = {
|
|||||||
animationTimeout: null
|
animationTimeout: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Track CodeMirror views for cleanup
|
||||||
|
let sectionCodeViews = [];
|
||||||
|
|
||||||
|
// Read-only CodeMirror theme for code examples
|
||||||
|
const readOnlyTheme = EditorView.theme(
|
||||||
|
{
|
||||||
|
"&": {
|
||||||
|
fontSize: "13px"
|
||||||
|
},
|
||||||
|
".cm-content": {
|
||||||
|
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||||
|
padding: "12px 0"
|
||||||
|
},
|
||||||
|
".cm-line": {
|
||||||
|
padding: "0 12px"
|
||||||
|
},
|
||||||
|
".cm-gutters": {
|
||||||
|
display: "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ dark: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight all code blocks in the section page using CodeMirror
|
||||||
|
*/
|
||||||
|
function highlightSectionCodeBlocks() {
|
||||||
|
// Clean up previous views
|
||||||
|
sectionCodeViews.forEach((view) => view.destroy());
|
||||||
|
sectionCodeViews = [];
|
||||||
|
|
||||||
|
// Find all code blocks in section page
|
||||||
|
const codeBlocks = elements.sectionIntro?.querySelectorAll(".code-block") || [];
|
||||||
|
|
||||||
|
codeBlocks.forEach((block) => {
|
||||||
|
const pre = block.querySelector("pre");
|
||||||
|
const code = block.querySelector("code");
|
||||||
|
if (!pre || !code) return;
|
||||||
|
|
||||||
|
const content = code.textContent || "";
|
||||||
|
|
||||||
|
// Detect language from content
|
||||||
|
const isHTML = content.includes("<") && content.includes(">");
|
||||||
|
const langExtension = isHTML ? html() : css();
|
||||||
|
|
||||||
|
// Create read-only CodeMirror view
|
||||||
|
const state = EditorState.create({
|
||||||
|
doc: content,
|
||||||
|
extensions: [langExtension, oneDark, readOnlyTheme, EditorState.readOnly.of(true), EditorView.lineWrapping]
|
||||||
|
});
|
||||||
|
|
||||||
|
const view = new EditorView({
|
||||||
|
state,
|
||||||
|
parent: block
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove original pre/code
|
||||||
|
pre.remove();
|
||||||
|
|
||||||
|
sectionCodeViews.push(view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// DOM elements - updated for new layout
|
// DOM elements - updated for new layout
|
||||||
const elements = {
|
const elements = {
|
||||||
// Header
|
// Header
|
||||||
@@ -1272,6 +1342,8 @@ function showSectionPage(sectionId) {
|
|||||||
// Inject educational content (includes integrated module links)
|
// Inject educational content (includes integrated module links)
|
||||||
if (elements.sectionIntro && sectionContent[sectionId]) {
|
if (elements.sectionIntro && sectionContent[sectionId]) {
|
||||||
elements.sectionIntro.innerHTML = sectionContent[sectionId];
|
elements.sectionIntro.innerHTML = sectionContent[sectionId];
|
||||||
|
// Highlight code blocks with CodeMirror
|
||||||
|
highlightSectionCodeBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get modules for this section to calculate progress
|
// Get modules for this section to calculate progress
|
||||||
|
|||||||
10
src/main.css
10
src/main.css
@@ -1911,6 +1911,7 @@ input:checked + .toggle-slider::before {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fallback styles for pre/code (before CodeMirror initializes) */
|
||||||
.code-block pre {
|
.code-block pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -1925,6 +1926,15 @@ input:checked + .toggle-slider::before {
|
|||||||
white-space: pre;
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CodeMirror styles within code blocks */
|
||||||
|
.code-block .cm-editor {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block .cm-scroller {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.code-caption {
|
.code-caption {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--light-text);
|
color: var(--light-text);
|
||||||
|
|||||||
Reference in New Issue
Block a user