58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
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 <mark> 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 });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
}
|