/** * SourceViewer hook — scrolls to and highlights the selected line. * * Attached to the `#source-viewer` div via `phx-hook="SourceViewer"`. * Reads `data-highlighted-line` to find the target line element and * scrolls it into view with a smooth animation. * Applies highlight.js syntax highlighting to individual line content elements. */ import hljs from "highlight.js/lib/core" export const SourceViewer = { mounted() { this.applySyntaxHighlighting() this.scrollToHighlighted() }, updated() { this.applySyntaxHighlighting() this.scrollToHighlighted() }, applySyntaxHighlighting() { const container = this.el.querySelector(".sv-lines") if (!container || container.querySelector(".hljs-on")) return; // Collect the raw text from all line content spans const lineContentEls = container.querySelectorAll(".sv-line-content") if (lineContentEls.length === 0) return; const rawText = Array.from(lineContentEls).map(el => el.textContent).join("\n") // Create a temp element for highlight.js to process const temp = document.createElement("code") temp.textContent = rawText hljs.highlightElement(temp) // Split the highlighted HTML back into lines // highlight.js innerHTML uses actual newlines between lines of code const highlightedLines = temp.innerHTML.split("\n") // Apply each highlighted line to the corresponding .sv-line-content lineContentEls.forEach((el, i) => { if (highlightedLines[i]) { el.innerHTML = highlightedLines[i] } }) // Mark as highlighted so we don't re-apply on subsequent updates container.classList.add("hljs", "hljs-on") }, scrollToHighlighted() { const lineNum = this.el.dataset.highlightedLine if (lineNum !== undefined && lineNum !== "") { const lineEl = document.getElementById(`line-${lineNum}`) if (lineEl) { lineEl.scrollIntoView({ behavior: "smooth", block: "center" }) } } } }