feat: integrate CodeMirror with custom macOS-style themes for code block syntax highlighting

This commit is contained in:
Ümit Tunç
2026-05-25 06:44:29 +03:00
parent 92f9147e80
commit c1dc4629c0
+215
View File
@@ -164,3 +164,218 @@
</div> </div>
</section> </section>
@endsection @endsection
@push('styles')
<!-- CodeMirror Styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css" integrity="sha384-zaeBlB/vwYsDRSlFajnDd7OydJ0cWk+c2OWybl3eSUf6hW2EbhlCsQPqKr3gkznT" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/material-darker.min.css" integrity="sha384-eZTPTN0EvJdn23s24UDYJmUM2T7C2ZFa3qFLypeBruJv8mZeTusKUAO/j5zPAQ6l" crossorigin="anonymous">
<!-- Google Fonts JetBrains Mono -->
<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=JetBrains+Mono:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
<style>
/* Custom modern code block window styling */
.code-window {
background-color: #212529;
border-radius: 12px;
box-shadow: 0 12px 24px rgba(30, 34, 40, 0.08), 0 4px 12px rgba(30, 34, 40, 0.04);
margin: 2rem 0;
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.08);
}
.code-window-header {
background-color: #1a1d20;
padding: 0.8rem 1.2rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.code-window-dots {
display: flex;
gap: 7px;
align-items: center;
}
.code-window-dot {
width: 11px;
height: 11px;
border-radius: 50%;
}
.dot-red { background-color: #ff5f56; }
.dot-yellow { background-color: #ffbd2e; }
.dot-green { background-color: #27c93f; }
.code-window-lang {
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #aab0bc;
background-color: rgba(255, 255, 255, 0.08);
padding: 3px 10px;
border-radius: 6px;
font-family: 'Space Grotesk', system-ui, sans-serif;
}
/* Customizing CodeMirror specifically */
.CodeMirror {
font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', Courier, monospace !important;
font-size: 0.85rem !important;
line-height: 1.65 !important;
height: auto !important;
background: #212529 !important;
padding: 0.8rem 0 !important;
}
.CodeMirror-scroll {
max-height: 550px !important;
}
.CodeMirror-gutters {
background-color: #212529 !important;
border-right: 1px solid rgba(255, 255, 255, 0.05) !important;
width: 48px !important;
}
.CodeMirror-linenumber {
color: #4a505e !important;
padding-left: 12px !important;
}
.CodeMirror-cursor {
border-left: 1px solid transparent !important; /* Hide cursor for read-only blocks */
}
/* Styling selection */
.CodeMirror-selected {
background: rgba(255, 255, 255, 0.1) !important;
}
.CodeMirror-focused .CodeMirror-selected {
background: rgba(255, 255, 255, 0.15) !important;
}
</style>
@endpush
@push('scripts')
<!-- CodeMirror Core JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js" integrity="sha384-uTzi9PU3axyLeKbgCPqQdNFT5Q23BRgFbmtrolYC7ROlbWVv7IggV+3r0ALSQd/s" crossorigin="anonymous"></script>
<!-- CodeMirror Modes -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/xml/xml.min.js" integrity="sha384-xPpkMo5nDgD98fIcuRVYhxkZV6/9Y4L8s3p0J5c4MxgJkyKJ8BJr+xfRkq7kn6Tw" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/javascript/javascript.min.js" integrity="sha384-g0o+WW9mdIxA7LaaCKTkRm0M5TVT+Bb4s9eocxPsI2G0Xm0POG9iD6G6qP1IIsfS" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/css/css.min.js" integrity="sha384-fpeIC2FZuPmw7mIsTvgB5BNc8QVxQC/nWg2W+CgPYOAiBiYVuHe2E8HiTWHBMIJQ" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/clike/clike.min.js" integrity="sha384-bZx++XMDqT0QRVoq6eVZnkq04LmwWZ0eLo0tFdSGkOJgH8qiqeMIIFJ+mRaM0wfb" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/php/php.min.js" integrity="sha384-1FUwPY2kaZKXw258/9CYBSS+zcc3CPggxE1zLjmYYiOdkcOw3KcXH5VNJWWbjw2U" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/python/python.min.js" integrity="sha384-Xy+2exU6lBoT4OpUOtnQb+cUpn+nlJQEHvRobWVtwz6wIsw4oNoO7xyd/l8rYgMy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/sql/sql.min.js" integrity="sha384-HxXmA1hLc56V6Ja4yfcCwAprmbnS4tuvKYS0qKG3t6oxOFMflcnYq5fOnt6wVCda" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/shell/shell.min.js" integrity="sha384-dmPRq54XNtYnlnAo7QrwJnZNa5r0JP5gU8z4H0686Cgz37qrR2jmNaDHUXyMI4wn" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/yaml/yaml.min.js" integrity="sha384-LekyoEGHzBMGUx26EEtC5G6HD6iyIr0msuy3LP3F8V98vVS5ZoNfIyiirXsoqAt/" crossorigin="anonymous"></script>
<!-- Initialization Script -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const codeBlocks = document.querySelectorAll('.post-content pre code');
if (codeBlocks.length === 0) return;
codeBlocks.forEach(function(codeEl) {
// Extract raw code text securely (safely handled by textContent)
const rawCode = codeEl.textContent || codeEl.innerText;
// Retrieve and parse language from class
let lang = 'code';
const classes = codeEl.className.split(' ');
for (const cls of classes) {
if (cls.startsWith('language-') || cls.startsWith('lang-')) {
lang = cls.replace('language-', '').replace('lang-', '');
break;
}
}
// Map languages to CodeMirror modes
const modeMap = {
'js': 'javascript',
'javascript': 'javascript',
'ts': 'javascript',
'typescript': 'javascript',
'json': 'javascript',
'html': 'xml',
'xml': 'xml',
'svg': 'xml',
'css': 'css',
'php': 'php',
'python': 'python',
'py': 'python',
'sql': 'sql',
'bash': 'shell',
'sh': 'shell',
'shell': 'shell',
'yaml': 'yaml',
'yml': 'yaml'
};
const resolvedMode = modeMap[lang.toLowerCase()] || 'javascript';
// Display nice label for the language badge
const displayLang = lang === 'code' ? 'Code' : lang;
// Build premium macOS-style window wrapping structure strictly using secure DOM methods (no innerHTML)
const preEl = codeEl.parentNode;
if (!preEl || preEl.tagName !== 'PRE') return;
const codeWindow = document.createElement('div');
codeWindow.className = 'code-window';
const header = document.createElement('div');
header.className = 'code-window-header';
const dots = document.createElement('div');
dots.className = 'code-window-dots';
const dotRed = document.createElement('div');
dotRed.className = 'code-window-dot dot-red';
const dotYellow = document.createElement('div');
dotYellow.className = 'code-window-dot dot-yellow';
const dotGreen = document.createElement('div');
dotGreen.className = 'code-window-dot dot-green';
dots.appendChild(dotRed);
dots.appendChild(dotYellow);
dots.appendChild(dotGreen);
const langBadge = document.createElement('div');
langBadge.className = 'code-window-lang';
langBadge.textContent = displayLang; // Secure text content insertion
header.appendChild(dots);
header.appendChild(langBadge);
const editorContainer = document.createElement('div');
editorContainer.className = 'code-editor-container';
codeWindow.appendChild(header);
codeWindow.appendChild(editorContainer);
// Replace the legacy pre tag with our beautiful new code window
preEl.parentNode.replaceChild(codeWindow, preEl);
// Instantiate CodeMirror inside the new container
CodeMirror(editorContainer, {
value: rawCode.trim(),
mode: resolvedMode,
theme: 'material-darker',
lineNumbers: true,
lineWrapping: true,
readOnly: 'nocursor',
viewportMargin: Infinity
});
});
});
</script>
@endpush