import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; import { visit } from "unist-util-visit"; import { VFile } from "vfile"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export async function detectWebGPU() { try { const adapter = await navigator.gpu.requestAdapter(); return !!adapter; } catch (e) { return false; } } /** * A custom rehype plugin that wraps the first occurrence of "textToHighlight" in a tag. * options: { textToHighlight: string } */ export default function rehypeHighlight(options: { textToHighlight: string }) { return (tree: any, file: VFile) => { let found = false; visit(tree, "text", (node: any) => { if (found) return; const { value } = node; const text = options.textToHighlight; const index = value.toLowerCase().indexOf(text.toLowerCase()); if (index !== -1) { found = true; // Split the text into three parts: before, match, and after. const before = value.slice(0, index); const match = value.slice(index, index + text.length); const after = value.slice(index + text.length); // Replace the current node with three nodes. node.type = "element"; node.tagName = "span"; node.properties = {}; node.children = []; if (before) { node.children.push({ type: "text", value: before }); } node.children.push({ type: "element", tagName: "mark", properties: { style: "background-color: yellow;" }, children: [{ type: "text", value: match }], }); if (after) { node.children.push({ type: "text", value: after }); } } }); }; }