/** * v-highlight directive * Lazy loads syntax highlighting for code blocks using highlight.js * Only highlights when element enters viewport (IntersectionObserver) */ import hljs from 'highlight.js/lib/core'; import json from 'highlight.js/lib/languages/json'; // Register JSON language hljs.registerLanguage('json', json); /** * Apply syntax highlighting to a code element * @param {HTMLElement} el - The code element to highlight * @param {string} theme - Theme name ('github' or 'github-dark') */ function applyHighlight(el, theme = 'github-dark') { if (el.classList.contains('hljs')) { // Already highlighted return; } try { hljs.highlightElement(el); // Remove any existing theme classes el.className = el.className.replace(/hljs-theme-\w+/, ''); el.classList.add(`hljs-theme-${theme}`); } catch (error) { console.error('Highlight.js error:', error); } } /** * Create IntersectionObserver for lazy highlighting * @param {HTMLElement} el - Element to observe * @param {string} theme - Theme to apply * @returns {IntersectionObserver} */ function createHighlightObserver(el, theme) { const observer = new IntersectionObserver( entries => { entries.forEach(entry => { if (entry.isIntersecting) { applyHighlight(entry.target, theme); observer.disconnect(); } }); }, { rootMargin: '50px', // Start highlighting slightly before entering viewport threshold: 0.01 } ); observer.observe(el); return observer; } /** * v-highlight directive * Usage: ... */ export const vHighlight = { mounted(el, binding) { const config = binding.value || {}; const theme = typeof config === 'string' ? config : (config.theme || 'github-dark'); // Store observer on element for cleanup el._highlightObserver = createHighlightObserver(el, theme); }, updated(el, binding) { const newConfig = binding.value || {}; const newTheme = typeof newConfig === 'string' ? newConfig : (newConfig.theme || 'github-dark'); const oldConfig = binding.oldValue || {}; const oldTheme = typeof oldConfig === 'string' ? oldConfig : (oldConfig.theme || 'github-dark'); // If theme changed, re-highlight if (newTheme !== oldTheme && el.classList.contains('hljs')) { el.classList.remove(`hljs-theme-${oldTheme}`); el.classList.add(`hljs-theme-${newTheme}`); } }, unmounted(el) { // Cleanup observer if (el._highlightObserver) { el._highlightObserver.disconnect(); delete el._highlightObserver; } } }; export default vHighlight;