mirror of
https://github.com/pheralb/svgl.git
synced 2025-12-29 08:01:36 +08:00
🛠️ Create rehypeCopyBtn & rehypeExternalLinks with custom types
This commit is contained in:
+12
-2
@@ -4,9 +4,13 @@ import { z } from "zod";
|
|||||||
import { compileMarkdown } from "@content-collections/markdown";
|
import { compileMarkdown } from "@content-collections/markdown";
|
||||||
import { defineCollection, defineConfig } from "@content-collections/core";
|
import { defineCollection, defineConfig } from "@content-collections/core";
|
||||||
|
|
||||||
// Shiki:
|
// Plugings:
|
||||||
|
import rehypeSlug from "rehype-slug";
|
||||||
|
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||||
import rehypeShiki from "@shikijs/rehype/core";
|
import rehypeShiki from "@shikijs/rehype/core";
|
||||||
import { shikiHighlighter, rehypeShikiOptions } from "./src/utils/shiki";
|
import { shikiHighlighter, rehypeShikiOptions } from "./src/utils/shiki";
|
||||||
|
import { rehypeCopyBtn } from "./src/markdown/rehypeCopyBtn";
|
||||||
|
import { rehypeExternalLinks } from "./src/markdown/rehypeExternalLinks";
|
||||||
|
|
||||||
const docs = defineCollection({
|
const docs = defineCollection({
|
||||||
name: "docs",
|
name: "docs",
|
||||||
@@ -19,7 +23,13 @@ const docs = defineCollection({
|
|||||||
transform: async (document, context) => {
|
transform: async (document, context) => {
|
||||||
const highlighter = await shikiHighlighter();
|
const highlighter = await shikiHighlighter();
|
||||||
const html = await compileMarkdown(context, document, {
|
const html = await compileMarkdown(context, document, {
|
||||||
rehypePlugins: [[rehypeShiki, highlighter, rehypeShikiOptions]],
|
rehypePlugins: [
|
||||||
|
rehypeSlug,
|
||||||
|
rehypeAutolinkHeadings,
|
||||||
|
[rehypeShiki, highlighter, rehypeShikiOptions],
|
||||||
|
rehypeExternalLinks,
|
||||||
|
rehypeCopyBtn,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...document,
|
...document,
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import type { UnistNode, UnistTree } from "@/types/unist";
|
||||||
|
|
||||||
|
import { visit } from "unist-util-visit";
|
||||||
|
import { cn } from "@/utils/cn";
|
||||||
|
|
||||||
|
export const rehypeCopyBtn = () => {
|
||||||
|
return (tree: UnistTree) => {
|
||||||
|
visit(tree, "element", (node: UnistNode, index, parent) => {
|
||||||
|
if (node.tagName === "pre" && parent && typeof index === "number") {
|
||||||
|
const copyIcon = {
|
||||||
|
type: "element",
|
||||||
|
tagName: "svg",
|
||||||
|
properties: {
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
width: "14",
|
||||||
|
height: "14",
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
strokeWidth: "2",
|
||||||
|
strokeLinecap: "round",
|
||||||
|
strokeLinejoin: "round",
|
||||||
|
style: "display: inline;",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "rect",
|
||||||
|
properties: {
|
||||||
|
width: "14",
|
||||||
|
height: "14",
|
||||||
|
x: "8",
|
||||||
|
y: "8",
|
||||||
|
rx: "2",
|
||||||
|
ry: "2",
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "path",
|
||||||
|
properties: {
|
||||||
|
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const successIcon = {
|
||||||
|
type: "element",
|
||||||
|
tagName: "svg",
|
||||||
|
properties: {
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
width: "14",
|
||||||
|
height: "14",
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
strokeWidth: "2",
|
||||||
|
strokeLinecap: "round",
|
||||||
|
strokeLinejoin: "round",
|
||||||
|
style: "display: none;",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "path",
|
||||||
|
properties: {
|
||||||
|
d: "M18 6 7 17l-5-5",
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "path",
|
||||||
|
properties: {
|
||||||
|
d: "m22 10-7.5 7.5L13 16",
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const copyButton = {
|
||||||
|
type: "element",
|
||||||
|
tagName: "button",
|
||||||
|
title: "Copy code to clipboard",
|
||||||
|
"aria-label": "Copy code to clipboard",
|
||||||
|
properties: {
|
||||||
|
type: "button",
|
||||||
|
title: "Copy code to clipboard",
|
||||||
|
class: cn(
|
||||||
|
"absolute top-2 right-2 px-1.5 py-0.5 rounded-md",
|
||||||
|
"bg-transparent hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
||||||
|
"transition-colors",
|
||||||
|
),
|
||||||
|
onclick: `
|
||||||
|
const button = this;
|
||||||
|
const copyIcon = button.querySelector('svg:first-child');
|
||||||
|
const successIcon = button.querySelector('svg:last-child');
|
||||||
|
const codeBlock = button.nextElementSibling;
|
||||||
|
navigator.clipboard.writeText(codeBlock.innerText).then(() => {
|
||||||
|
copyIcon.style.display = 'none';
|
||||||
|
successIcon.style.display = 'inline';
|
||||||
|
setTimeout(() => {
|
||||||
|
copyIcon.style.display = 'inline';
|
||||||
|
successIcon.style.display = 'none';
|
||||||
|
}, 2000);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error('Error copying:', err);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
children: [copyIcon, successIcon],
|
||||||
|
};
|
||||||
|
const wrapper = {
|
||||||
|
type: "element",
|
||||||
|
tagName: "div",
|
||||||
|
properties: { class: "relative" },
|
||||||
|
children: [copyButton, node],
|
||||||
|
};
|
||||||
|
parent.children[index] = wrapper;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { UnistNode, UnistTree } from "@/types/unist";
|
||||||
|
import { visit } from "unist-util-visit";
|
||||||
|
|
||||||
|
const APP_DOMAIN = "svgl.app";
|
||||||
|
|
||||||
|
export const rehypeExternalLinks = () => {
|
||||||
|
return (tree: UnistTree) => {
|
||||||
|
visit(tree, "element", (node: UnistNode) => {
|
||||||
|
if (node.tagName === "a" && node.properties?.href) {
|
||||||
|
const href = String(node.properties.href);
|
||||||
|
if (!href.includes(APP_DOMAIN)) {
|
||||||
|
node.properties.target = "_blank";
|
||||||
|
node.properties.rel = "noopener noreferrer";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export interface UnistNode {
|
||||||
|
type: string;
|
||||||
|
name?: string;
|
||||||
|
tagName?: string;
|
||||||
|
value?: string;
|
||||||
|
properties?: Record<string, unknown>;
|
||||||
|
attributes?: {
|
||||||
|
name: string;
|
||||||
|
value: unknown;
|
||||||
|
type?: string;
|
||||||
|
}[];
|
||||||
|
children?: UnistNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnistTree {
|
||||||
|
type: string;
|
||||||
|
children: UnistNode[];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user