Add syntax highlighting and light/dark themes to twui CodeBlock
- Highlight string children with refractor (Prism) when `language` is set, rendered as React token spans; add ini/bash/json/yaml/ts support - Add `theme` prop (auto | light | dark); auto follows the `.dark` class. Palettes are `--twui-code-*` CSS variables (GitHub Light/Dark, all tokens WCAG AA) so custom themes need no component changes - Fix layout: real padding, singleBlock copy button no longer covers the code, clipped rounded corners, wrapperProps.style honoured, long tokens wrap - MDX helpers take `codeTheme` and no longer force a black background - Highlight WireGuard config blocks as ini Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
"openai": "^7.4.0",
|
"openai": "^7.4.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
|
"refractor": "^5.0.0",
|
||||||
"rehype-prism-plus": "^2.0.2",
|
"rehype-prism-plus": "^2.0.2",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"sharp": "^0.35.3",
|
"sharp": "^0.35.3",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"openai": "^7.4.0",
|
"openai": "^7.4.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
|
"refractor": "^5.0.0",
|
||||||
"rehype-prism-plus": "^2.0.2",
|
"rehype-prism-plus": "^2.0.2",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"sharp": "^0.35.3",
|
"sharp": "^0.35.3",
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { refractor } from "refractor/core";
|
||||||
|
import bash from "refractor/bash";
|
||||||
|
import ini from "refractor/ini";
|
||||||
|
import javascript from "refractor/javascript";
|
||||||
|
import json from "refractor/json";
|
||||||
|
import typescript from "refractor/typescript";
|
||||||
|
import yaml from "refractor/yaml";
|
||||||
|
import type { Root } from "hast";
|
||||||
|
|
||||||
|
refractor.register(bash);
|
||||||
|
refractor.register(ini);
|
||||||
|
refractor.register(javascript);
|
||||||
|
refractor.register(json);
|
||||||
|
refractor.register(typescript);
|
||||||
|
refractor.register(yaml);
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
code: string;
|
||||||
|
language?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Highlight a code string with Prism (via refractor)
|
||||||
|
*
|
||||||
|
* Returns a hast tree of `.token` spans, or `null` when the
|
||||||
|
* language is missing or not registered.
|
||||||
|
*/
|
||||||
|
export default function highlightCode({ code, language }: Params): Root | null {
|
||||||
|
if (!language || !refractor.registered(language)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return refractor.highlight(code, language);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { RootContent } from "hast";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
node: RootContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Render one node of a refractor hast tree
|
||||||
|
*/
|
||||||
|
export default function CodeBlockHastNode({ node }: Props) {
|
||||||
|
if (node.type === "text") {
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type !== "element") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const className = node.properties?.className;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
Array.isArray(className) ? className.join(" ") : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{node.children.map((child, index) => (
|
||||||
|
<CodeBlockHastNode key={index} node={child} />
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -172,3 +172,111 @@ ul,
|
|||||||
ol {
|
ol {
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* CodeBlock themes */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* `.twui-code-block-theme-auto` (default) follows the `.dark` class on <html>;
|
||||||
|
* `-light` / `-dark` pin a palette. Override the variables for a custom theme.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.twui-code-block-wrapper {
|
||||||
|
/* GitHub Light — every token >= 4.5:1 (WCAG AA) on the background */
|
||||||
|
--twui-code-bg: #f6f8fa;
|
||||||
|
--twui-code-fg: #1f2328;
|
||||||
|
--twui-code-muted: #59636e;
|
||||||
|
--twui-code-border: rgb(31 35 40 / 0.15);
|
||||||
|
--twui-code-comment: #59636e;
|
||||||
|
--twui-code-punctuation: #1f2328;
|
||||||
|
--twui-code-red: #cf222e;
|
||||||
|
--twui-code-blue: #0550ae;
|
||||||
|
--twui-code-green: #116329;
|
||||||
|
--twui-code-orange: #953800;
|
||||||
|
--twui-code-purple: #8250df;
|
||||||
|
--twui-code-yellow: #7d4e00;
|
||||||
|
--twui-code-cyan: #0a3069;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .twui-code-block-wrapper.twui-code-block-theme-auto,
|
||||||
|
.twui-code-block-wrapper.twui-code-block-theme-dark {
|
||||||
|
/* GitHub Dark — every token >= 6.5:1 on the background */
|
||||||
|
--twui-code-bg: #0d1117;
|
||||||
|
--twui-code-fg: #e6edf3;
|
||||||
|
--twui-code-muted: #9198a1;
|
||||||
|
--twui-code-border: rgb(240 246 252 / 0.1);
|
||||||
|
--twui-code-comment: #9198a1;
|
||||||
|
--twui-code-punctuation: #c9d1d9;
|
||||||
|
--twui-code-red: #ff7b72;
|
||||||
|
--twui-code-blue: #79c0ff;
|
||||||
|
--twui-code-green: #7ee787;
|
||||||
|
--twui-code-orange: #ffa657;
|
||||||
|
--twui-code-purple: #d2a8ff;
|
||||||
|
--twui-code-yellow: #e3b341;
|
||||||
|
--twui-code-cyan: #a5d6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* CodeBlock syntax highlighting */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.comment,
|
||||||
|
.twui-code-block-pre .token.prolog,
|
||||||
|
.twui-code-block-pre .token.doctype,
|
||||||
|
.twui-code-block-pre .token.cdata {
|
||||||
|
color: var(--twui-code-comment);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.punctuation {
|
||||||
|
color: var(--twui-code-punctuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.section,
|
||||||
|
.twui-code-block-pre .token.selector,
|
||||||
|
.twui-code-block-pre .token.tag,
|
||||||
|
.twui-code-block-pre .token.deleted {
|
||||||
|
color: var(--twui-code-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.key,
|
||||||
|
.twui-code-block-pre .token.property,
|
||||||
|
.twui-code-block-pre .token.attr-name,
|
||||||
|
.twui-code-block-pre .token.variable,
|
||||||
|
.twui-code-block-pre .token.parameter {
|
||||||
|
color: var(--twui-code-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.string,
|
||||||
|
.twui-code-block-pre .token.attr-value,
|
||||||
|
.twui-code-block-pre .token.value,
|
||||||
|
.twui-code-block-pre .token.char,
|
||||||
|
.twui-code-block-pre .token.inserted {
|
||||||
|
color: var(--twui-code-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.number,
|
||||||
|
.twui-code-block-pre .token.boolean,
|
||||||
|
.twui-code-block-pre .token.constant,
|
||||||
|
.twui-code-block-pre .token.symbol {
|
||||||
|
color: var(--twui-code-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.keyword,
|
||||||
|
.twui-code-block-pre .token.important,
|
||||||
|
.twui-code-block-pre .token.atrule {
|
||||||
|
color: var(--twui-code-purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.function,
|
||||||
|
.twui-code-block-pre .token.class-name,
|
||||||
|
.twui-code-block-pre .token.builtin {
|
||||||
|
color: var(--twui-code-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twui-code-block-pre .token.operator,
|
||||||
|
.twui-code-block-pre .token.entity,
|
||||||
|
.twui-code-block-pre .token.url,
|
||||||
|
.twui-code-block-pre .token.regex {
|
||||||
|
color: var(--twui-code-cyan);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,23 @@ import Stack from "../layout/Stack";
|
|||||||
import Row from "../layout/Row";
|
import Row from "../layout/Row";
|
||||||
import Button from "../layout/Button";
|
import Button from "../layout/Button";
|
||||||
import Divider from "../layout/Divider";
|
import Divider from "../layout/Divider";
|
||||||
|
import highlightCode from "../(functions)/code-block/highlight-code";
|
||||||
|
import CodeBlockHastNode from "../(partials)/CodeBlockHastNode";
|
||||||
|
|
||||||
export const TWUIPrismLanguages = ["shell", "javascript"] as const;
|
export const TWUIPrismLanguages = [
|
||||||
|
"shell",
|
||||||
|
"bash",
|
||||||
|
"ini",
|
||||||
|
"javascript",
|
||||||
|
"typescript",
|
||||||
|
"json",
|
||||||
|
"yaml",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/** `auto` follows the `.dark` class on `<html>`; `light`/`dark` pin the palette */
|
||||||
|
export const TWUICodeBlockThemes = ["auto", "light", "dark"] as const;
|
||||||
|
|
||||||
|
export type TWUICodeBlockTheme = (typeof TWUICodeBlockThemes)[number];
|
||||||
|
|
||||||
type Props = PropsWithChildren &
|
type Props = PropsWithChildren &
|
||||||
DetailedHTMLProps<HTMLAttributes<HTMLPreElement>, HTMLPreElement> & {
|
DetailedHTMLProps<HTMLAttributes<HTMLPreElement>, HTMLPreElement> & {
|
||||||
@@ -22,6 +37,8 @@ type Props = PropsWithChildren &
|
|||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
singleBlock?: boolean;
|
singleBlock?: boolean;
|
||||||
language?: (typeof TWUIPrismLanguages)[number];
|
language?: (typeof TWUIPrismLanguages)[number];
|
||||||
|
/** Colour palette. Defaults to `auto` (follows the page theme) */
|
||||||
|
theme?: TWUICodeBlockTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +48,10 @@ type Props = PropsWithChildren &
|
|||||||
* @className `twui-code-pre-wrapper`
|
* @className `twui-code-pre-wrapper`
|
||||||
* @className `twui-code-block-pre`
|
* @className `twui-code-block-pre`
|
||||||
* @className `twui-code-block-header`
|
* @className `twui-code-block-header`
|
||||||
|
* @className `twui-code-block-theme-{auto|light|dark}`
|
||||||
|
*
|
||||||
|
* Colours come from `--twui-code-*` CSS variables (see `base.css`), so a
|
||||||
|
* custom palette is a matter of overriding them on the wrapper.
|
||||||
*/
|
*/
|
||||||
export default function CodeBlock({
|
export default function CodeBlock({
|
||||||
children,
|
children,
|
||||||
@@ -38,6 +59,7 @@ export default function CodeBlock({
|
|||||||
backgroundColor,
|
backgroundColor,
|
||||||
singleBlock,
|
singleBlock,
|
||||||
language,
|
language,
|
||||||
|
theme = "auto",
|
||||||
...props
|
...props
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const codeRef = React.useRef<HTMLDivElement>(null);
|
const codeRef = React.useRef<HTMLDivElement>(null);
|
||||||
@@ -46,24 +68,33 @@ export default function CodeBlock({
|
|||||||
|
|
||||||
const title = props?.["data-title"];
|
const title = props?.["data-title"];
|
||||||
|
|
||||||
const finalBackgroundColor = backgroundColor || "#28272b";
|
const highlighted = React.useMemo(
|
||||||
|
() =>
|
||||||
|
typeof children === "string"
|
||||||
|
? highlightCode({ code: children, language })
|
||||||
|
: null,
|
||||||
|
[children, language],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...wrapperProps}
|
{...wrapperProps}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"outline-[1px] outline-slate-200 dark:outline-white/10",
|
"outline-[1px] outline-(--twui-code-border) bg-(--twui-code-bg)",
|
||||||
`rounded w-full transition-all items-start`,
|
`rounded w-full transition-all overflow-hidden`,
|
||||||
"relative max-w-[80vw] sm:max-w-[85vw] xl:max-w-[880px]",
|
"relative max-w-[80vw] sm:max-w-[85vw] xl:max-w-[880px]",
|
||||||
"twui-code-block-wrapper",
|
"twui-code-block-wrapper",
|
||||||
|
`twui-code-block-theme-${theme}`,
|
||||||
wrapperProps?.className,
|
wrapperProps?.className,
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: copied
|
boxShadow: copied
|
||||||
? "0 0 10px 10px rgba(18, 139, 99, 0.2)"
|
? "0 0 10px 10px rgba(18, 139, 99, 0.2)"
|
||||||
: undefined,
|
: undefined,
|
||||||
backgroundColor: finalBackgroundColor,
|
...(backgroundColor
|
||||||
...props.style,
|
? ({ "--twui-code-bg": backgroundColor } as React.CSSProperties)
|
||||||
|
: {}),
|
||||||
|
...wrapperProps?.style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack
|
||||||
@@ -74,19 +105,22 @@ export default function CodeBlock({
|
|||||||
>
|
>
|
||||||
<Row
|
<Row
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"w-full px-1 h-10 sticky top-0 py-2",
|
"flex-nowrap z-10",
|
||||||
singleBlock ? "absolute !bg-transparent" : "",
|
singleBlock
|
||||||
|
? "absolute top-0 right-0 p-1.5"
|
||||||
|
: "w-full h-10 px-3 sticky top-0 shrink-0 bg-(--twui-code-bg)",
|
||||||
"twui-code-block-header",
|
"twui-code-block-header",
|
||||||
)}
|
)}
|
||||||
style={{
|
|
||||||
backgroundColor: finalBackgroundColor,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{title && <span className="text-white/70">{title}</span>}
|
{title && (
|
||||||
|
<span className="text-(--twui-code-muted) text-xs truncate">
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<div className="ml-auto">
|
<div className="ml-auto">
|
||||||
{copied ? (
|
{copied ? (
|
||||||
<Row>
|
<Row>
|
||||||
<span className="text-white text-xs twui-code-block-copied-text">
|
<span className="text-(--twui-code-fg) text-xs twui-code-block-copied-text">
|
||||||
Copied!
|
Copied!
|
||||||
</span>
|
</span>
|
||||||
<div className="w-5 h-5 rounded-full bg-emerald-600 text-white flex items-center justify-center">
|
<div className="w-5 h-5 rounded-full bg-emerald-600 text-white flex items-center justify-center">
|
||||||
@@ -97,8 +131,8 @@ export default function CodeBlock({
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="gray"
|
color="gray"
|
||||||
beforeIcon={<Copy size={17} color="white" />}
|
beforeIcon={<Copy size={17} />}
|
||||||
className="!p-1 !bg-transparent opacity-50"
|
className="!p-1 !bg-transparent !text-(--twui-code-muted) hover:!text-(--twui-code-fg)"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const content =
|
const content =
|
||||||
codeRef.current?.textContent;
|
codeRef.current?.textContent;
|
||||||
@@ -122,12 +156,12 @@ export default function CodeBlock({
|
|||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
{!singleBlock && (
|
{!singleBlock && (
|
||||||
<Divider className="!border-white/10 sticky top-10" />
|
<Divider className="!border-(--twui-code-border) sticky top-10 z-10 shrink-0" />
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
`p-1 w-full [&_pre]:!bg-transparent`,
|
`px-3 py-2.5 w-full [&_pre]:!bg-transparent`,
|
||||||
singleBlock ? "" : "-mt-1",
|
singleBlock ? "pr-11" : "",
|
||||||
"twui-code-pre-wrapper",
|
"twui-code-pre-wrapper",
|
||||||
)}
|
)}
|
||||||
ref={codeRef as any}
|
ref={codeRef as any}
|
||||||
@@ -135,13 +169,26 @@ export default function CodeBlock({
|
|||||||
<pre
|
<pre
|
||||||
{...props}
|
{...props}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"!my-0 whitespace-pre-wrap",
|
"!my-0 whitespace-pre-wrap break-words text-(--twui-code-fg)",
|
||||||
language ? `language-${language}` : "",
|
language ? `language-${language}` : "",
|
||||||
"twui-code-block-pre",
|
"twui-code-block-pre",
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
<code
|
||||||
|
className={
|
||||||
|
language ? `language-${language}` : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{highlighted
|
||||||
|
? highlighted.children.map((node, index) => (
|
||||||
|
<CodeBlockHastNode
|
||||||
|
key={index}
|
||||||
|
node={node}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
: children}
|
||||||
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type MDXComponents } from "mdx/types";
|
import { type MDXComponents } from "mdx/types";
|
||||||
import CodeBlock from "../elements/CodeBlock";
|
import CodeBlock, { type TWUICodeBlockTheme } from "../elements/CodeBlock";
|
||||||
import H1 from "../layout/H1";
|
import H1 from "../layout/H1";
|
||||||
import H4 from "../layout/H4";
|
import H4 from "../layout/H4";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -11,10 +11,12 @@ import H3 from "../layout/H3";
|
|||||||
type Params = {
|
type Params = {
|
||||||
components: MDXComponents;
|
components: MDXComponents;
|
||||||
codeBgColor?: string;
|
codeBgColor?: string;
|
||||||
|
codeTheme?: TWUICodeBlockTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useMDXComponents(params?: Params) {
|
export function useMDXComponents(params?: Params) {
|
||||||
const codeBgColor = params?.codeBgColor || "#000000";
|
const codeBgColor = params?.codeBgColor;
|
||||||
|
const codeTheme = params?.codeTheme;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
components: {
|
components: {
|
||||||
@@ -24,7 +26,9 @@ export function useMDXComponents(params?: Params) {
|
|||||||
if (React.isValidElement(children) && children.props) {
|
if (React.isValidElement(children) && children.props) {
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<CodeBlock {...props} backgroundColor={codeBgColor}>
|
<CodeBlock {...props} backgroundColor={codeBgColor}
|
||||||
|
theme={codeTheme}
|
||||||
|
>
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
{children.props.children}
|
{children.props.children}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
@@ -32,7 +36,9 @@ export function useMDXComponents(params?: Params) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<CodeBlock {...props} backgroundColor={codeBgColor}>
|
<CodeBlock {...props} backgroundColor={codeBgColor}
|
||||||
|
theme={codeTheme}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,17 +4,19 @@ import H1 from "../../layout/H1";
|
|||||||
import H2 from "../../layout/H2";
|
import H2 from "../../layout/H2";
|
||||||
import H3 from "../../layout/H3";
|
import H3 from "../../layout/H3";
|
||||||
import H4 from "../../layout/H4";
|
import H4 from "../../layout/H4";
|
||||||
import CodeBlock from "../../elements/CodeBlock";
|
import CodeBlock, { type TWUICodeBlockTheme } from "../../elements/CodeBlock";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
components: MDXComponents;
|
components: MDXComponents;
|
||||||
codeBgColor?: string;
|
codeBgColor?: string;
|
||||||
|
codeTheme?: TWUICodeBlockTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function useMDXComponents({
|
export default function useMDXComponents({
|
||||||
components,
|
components,
|
||||||
codeBgColor,
|
codeBgColor,
|
||||||
|
codeTheme,
|
||||||
}: Params): MDXComponents {
|
}: Params): MDXComponents {
|
||||||
return {
|
return {
|
||||||
h1: ({ children }) => <H1>{children}</H1>,
|
h1: ({ children }) => <H1>{children}</H1>,
|
||||||
@@ -23,7 +25,9 @@ export default function useMDXComponents({
|
|||||||
if (React.isValidElement(children) && children.props) {
|
if (React.isValidElement(children) && children.props) {
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<CodeBlock {...props} backgroundColor={codeBgColor}>
|
<CodeBlock {...props} backgroundColor={codeBgColor}
|
||||||
|
theme={codeTheme}
|
||||||
|
>
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
{children.props.children}
|
{children.props.children}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
@@ -31,7 +35,9 @@ export default function useMDXComponents({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<CodeBlock {...props} backgroundColor={codeBgColor}>
|
<CodeBlock {...props} backgroundColor={codeBgColor}
|
||||||
|
theme={codeTheme}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
"next-mdx-remote": "latest",
|
"next-mdx-remote": "latest",
|
||||||
"remark-gfm": "latest",
|
"remark-gfm": "latest",
|
||||||
"rehype-prism-plus": "latest",
|
"rehype-prism-plus": "latest",
|
||||||
"html-to-react": "^1.7.0"
|
"html-to-react": "^1.7.0",
|
||||||
|
"refractor": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ace": "latest",
|
"@types/ace": "latest",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function HostInterfaceSection({ host, clients }: Props) {
|
|||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CodeBlock wrapperProps={{ className: "max-w-full!" }}>
|
<CodeBlock language="ini" wrapperProps={{ className: "max-w-full!" }}>
|
||||||
{buildInterfaceSummary({ config })}
|
{buildInterfaceSummary({ config })}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
+3
-1
@@ -29,7 +29,9 @@ export default function ClientConfigPanel() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{config ? (
|
{config ? (
|
||||||
<CodeBlock wrapperProps={{ className: "max-w-full!" }}>{config}</CodeBlock>
|
<CodeBlock language="ini" wrapperProps={{ className: "max-w-full!" }}>
|
||||||
|
{config}
|
||||||
|
</CodeBlock>
|
||||||
) : (
|
) : (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
className="py-8"
|
className="py-8"
|
||||||
|
|||||||
+1
-1
@@ -231,7 +231,7 @@ span code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.twui-code-block-pre {
|
.twui-code-block-pre {
|
||||||
@apply font-mono text-sm leading-relaxed text-zinc-100;
|
@apply font-mono text-sm leading-relaxed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inputs: label sits above the field instead of floating on the border */
|
/* Inputs: label sits above the field instead of floating on the border */
|
||||||
|
|||||||
Reference in New Issue
Block a user