-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathroot-wrapper.js
More file actions
136 lines (125 loc) · 4.18 KB
/
Copy pathroot-wrapper.js
File metadata and controls
136 lines (125 loc) · 4.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import React from "react";
import { MDXProvider } from "@mdx-js/react";
import Code from "./src/components/CodeBlock";
import CTA_ImageOnly from "./src/components/Call-To-Actions/CTA_ImageOnly";
import CTA_FullWidth from "./src/components/Call-To-Actions/CTA_FullWidth";
import CTA_Bottom from "./src/components/Call-To-Actions/CTA_Bottom";
import { ContextWrapper } from "./context-wrapper";
import { IoIosCopy } from "@react-icons/all-files/io/IoIosCopy";
import { IoIosCheckmark } from "@react-icons/all-files/io/IoIosCheckmark";
// Custom image component for better CLS scores
const OptimizedImage = (props) => {
return (
<div style={{ width: "100%", height: "auto" }}>
<img
{...props}
width={props.width || "100%"}
height={props.height || "auto"}
style={{
objectFit: props.objectFit || "contain",
margin: "20px 0px",
...props.style,
}}
loading="lazy"
alt={props.alt || "Blog content image"}
/>
</div>
);
};
// Returns true only for commands like `npm install`, `npx skill install`
// Returns false for paths like `~/.claude/skills/`, flags like `-sfn`
const isCommand = (text) => {
const str = String(text).trim();
return !/^[~/.]/.test(str);
};
// Inline code component with copy-on-hover button
const InlineCode = ({ children }) => {
const [copied, setCopied] = React.useState(false);
const [hovered, setHovered] = React.useState(false);
const codeRef = React.useRef(null);
const showCopyButton = isCommand(children);
const handleCopy = () => {
const text = codeRef.current?.textContent || children;
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<span
style={{
position: "relative",
display: "inline-flex",
alignItems: "center",
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<code ref={codeRef}>{children}</code>
{showCopyButton && (
<span
style={{
display: "inline-block",
width: hovered ? "22px" : "0px",
marginLeft: hovered ? "4px" : "0px",
overflow: "hidden",
transition: "width 0.2s ease, margin 0.2s ease",
verticalAlign: "middle",
}}
>
<button
onClick={handleCopy}
title="Copy to clipboard"
style={{
background: "transparent",
border: "none",
cursor: "pointer",
padding: "2px 2px",
color: "inherit",
opacity: hovered ? 1 : 0,
transition: "opacity 0.2s ease",
borderRadius: "3px",
display: "flex",
alignItems: "center",
}}
aria-label="Copy to clipboard"
>
{copied ? <IoIosCheckmark size={16} /> : <IoIosCopy size={14} />}
</button>
</span>
)}
</span>
);
};
// A fenced code block compiles to <pre><code className="language-x">text</code></pre>.
// MDX v1 tagged that child with `mdxType: "code"`; MDX v2+ does not, so matching
// on it rendered every fenced block as nothing. Detect the shape instead, and
// fall back to a plain <pre> rather than returning undefined.
const FencedCode = ({ children, ...preProps }) => {
const child = React.Children.toArray(children)[0];
const code = React.isValidElement(child) ? child.props.children : undefined;
if (typeof code !== "string") {
return <pre {...preProps}>{children}</pre>;
}
// Drop only the newline the MDX compiler appends; trim() would also strip
// meaningful indentation on the first line (e.g. a nested YAML snippet).
const language = child.props.className?.replace(/^language-/, "");
return (
<Code
codeString={code.replace(/\r?\n$/, "")}
language={language || undefined}
/>
);
};
const components = {
pre: FencedCode,
img: OptimizedImage,
code: InlineCode,
CTA_ImageOnly,
CTA_FullWidth,
CTA_Bottom,
};
export const wrapRootElement = ({ element }) => (
<ContextWrapper>
<MDXProvider components={components}>{element}</MDXProvider>
</ContextWrapper>
);