// assets/js/hooks/code_block_hook.js import { codeToHtml } from "shiki"; const CodeBlockHook = { mounted() { const code = this.el.dataset.code; const language = this.el.dataset.language; const highlightedLines = JSON.parse( this.el.dataset.highlightedLines || "[]", ); const lines = code.split("\n"); console.log(lines.length, lines); // Convert line numbers to decorations const decorations = highlightedLines .map((line) => { // Convert to 0-based index and ensure valid line number const lineIndex = line - 1; if (lineIndex < 0 || lineIndex >= lines.length) return null; // Get the actual line length const lineLength = lines[lineIndex].length; return { // Line numbers are 0-indexed start: { line: lineIndex, character: 0 }, end: { line: lineIndex, character: lineLength }, properties: { // Apply both background color and a class for flexibility class: "highlight", style: "background-color: rgba(200,200,255,0.1);", }, }; }) .filter(Boolean); // Remove any null entries from invalid line numbers codeToHtml(code, { lang: language, theme: "catppuccin-mocha", transformers: [ { line(node, line) { node.properties["data-line"] = line; if (highlightedLines.includes(line)) { this.addClassToHast(node, "highlighted"); } }, }, ], }).then((html) => { console.log(html); // Replace the code content while preserving the pre/code structure const tempDiv = document.createElement("div"); tempDiv.innerHTML = html; const codeContent = tempDiv.querySelector("code"); if (codeContent) { this.el.querySelector("code").innerHTML = codeContent.innerHTML; } }); }, }; export default CodeBlockHook;