mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Add syntax highlighting to the Markdown element (#356)
Also fixes the styling for the copy button
This commit is contained in:
@@ -2,9 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.copy-button {
|
||||
padding: 5px 5px;
|
||||
padding: 5px 5px !important;
|
||||
opacity: 0.5;
|
||||
|
||||
.fa-check {
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.copied {
|
||||
opacity: 1;
|
||||
color: var(--success-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { clsx } from "clsx";
|
||||
import * as React from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Button } from "./button";
|
||||
|
||||
import "./copybutton.less";
|
||||
|
||||
type CopyButtonProps = {
|
||||
@@ -14,8 +13,8 @@ type CopyButtonProps = {
|
||||
};
|
||||
|
||||
const CopyButton = ({ title, className, onClick }: CopyButtonProps) => {
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (isCopied) {
|
||||
@@ -35,7 +34,7 @@ const CopyButton = ({ title, className, onClick }: CopyButtonProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
@@ -44,7 +43,11 @@ const CopyButton = ({ title, className, onClick }: CopyButtonProps) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Button onClick={handleOnClick} className={clsx("copy-button secondary ghost", className)} title={title}>
|
||||
<Button
|
||||
onClick={handleOnClick}
|
||||
className={clsx("copy-button secondary ghost", className, { copied: isCopied })}
|
||||
title={title}
|
||||
>
|
||||
{isCopied ? <i className="fa-sharp fa-solid fa-check"></i> : <i className="fa-sharp fa-solid fa-copy"></i>}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -81,13 +81,11 @@
|
||||
visibility: hidden;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
i {
|
||||
color: var(--line-actions-inactive-color);
|
||||
@@ -118,7 +116,7 @@
|
||||
|
||||
code {
|
||||
color: var(--app-text-color);
|
||||
background-color: var(--panel-bg-color);
|
||||
background-color: transparent;
|
||||
font-family: var(--termfontfamily);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import { Atom } from "jotai";
|
||||
import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ReactMarkdown, { Components } from "react-markdown";
|
||||
import rehypeHighlight from "rehype-highlight";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
import RemarkFlexibleToc, { TocItem } from "remark-flexible-toc";
|
||||
import remarkGfm from "remark-gfm";
|
||||
@@ -47,8 +49,8 @@ const Heading = ({ props, hnum }: { props: React.HTMLAttributes<HTMLHeadingEleme
|
||||
);
|
||||
};
|
||||
|
||||
const Code = ({ children }: { children: React.ReactNode }) => {
|
||||
return <code>{children}</code>;
|
||||
const Code = ({ className, children }: { className: string; children: React.ReactNode }) => {
|
||||
return <code className={className}>{children}</code>;
|
||||
};
|
||||
|
||||
type CodeBlockProps = {
|
||||
@@ -235,7 +237,26 @@ const Markdown = ({ text, textAtom, showTocAtom, style, className, resolveOpts,
|
||||
>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, [RemarkFlexibleToc, { tocRef: tocRef.current }]]}
|
||||
rehypePlugins={[rehypeRaw, () => rehypeSlug({ prefix: idPrefix })]}
|
||||
rehypePlugins={[
|
||||
rehypeRaw,
|
||||
() => rehypeSlug({ prefix: idPrefix }),
|
||||
rehypeHighlight,
|
||||
() =>
|
||||
rehypeSanitize({
|
||||
...defaultSchema,
|
||||
attributes: {
|
||||
...defaultSchema.attributes,
|
||||
span: [
|
||||
...(defaultSchema.attributes?.span || []),
|
||||
// Allow all class names starting with `hljs-`.
|
||||
["className", /^hljs-./],
|
||||
// Alternatively, to allow only certain class names:
|
||||
// ['className', 'hljs-number', 'hljs-title', 'hljs-variable']
|
||||
],
|
||||
},
|
||||
tagNames: [...(defaultSchema.tagNames || []), "span"],
|
||||
}),
|
||||
]}
|
||||
components={markdownComponents}
|
||||
>
|
||||
{text}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Used for syntax highlighting in markdown
|
||||
@import url("../../node_modules/highlight.js/styles/github-dark-dimmed.min.css");
|
||||
|
||||
:root {
|
||||
--main-text-color: #f7f7f7;
|
||||
--title-font-size: 18px;
|
||||
|
||||
@@ -112,7 +112,9 @@
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-gauge-chart": "^0.5.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"rehype-highlight": "^7.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-flexible-toc": "^1.1.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
|
||||
@@ -3967,7 +3967,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ungap/structured-clone@npm:^1.0.0":
|
||||
"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@ungap/structured-clone@npm:1.2.0"
|
||||
checksum: 10c0/8209c937cb39119f44eb63cf90c0b73e7c754209a6411c707be08e50e29ee81356dca1a848a405c8bdeebfe2f5e4f831ad310ae1689eeef65e7445c090c6657d
|
||||
@@ -7659,6 +7659,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hast-util-sanitize@npm:^5.0.0":
|
||||
version: 5.0.1
|
||||
resolution: "hast-util-sanitize@npm:5.0.1"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
"@ungap/structured-clone": "npm:^1.2.0"
|
||||
unist-util-position: "npm:^5.0.0"
|
||||
checksum: 10c0/30b3e69effcd6c3c6b40264a169dca9ab6568d98b9aeaa75ee5e0a6b3c7f9843fe6bc9c2cb11c24172b32c2ab7d88e0f243e72a9526107ba35ca58f410cd7ca5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hast-util-to-jsx-runtime@npm:^2.0.0":
|
||||
version: 2.3.0
|
||||
resolution: "hast-util-to-jsx-runtime@npm:2.3.0"
|
||||
@@ -7706,6 +7717,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hast-util-to-text@npm:^4.0.0":
|
||||
version: 4.0.2
|
||||
resolution: "hast-util-to-text@npm:4.0.2"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
"@types/unist": "npm:^3.0.0"
|
||||
hast-util-is-element: "npm:^3.0.0"
|
||||
unist-util-find-after: "npm:^5.0.0"
|
||||
checksum: 10c0/93ecc10e68fe5391c6e634140eb330942e71dea2724c8e0c647c73ed74a8ec930a4b77043b5081284808c96f73f2bee64ee416038ece75a63a467e8d14f09946
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hast-util-whitespace@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "hast-util-whitespace@npm:3.0.0"
|
||||
@@ -7728,6 +7751,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"highlight.js@npm:~11.9.0":
|
||||
version: 11.9.0
|
||||
resolution: "highlight.js@npm:11.9.0"
|
||||
checksum: 10c0/27cfa8717dc9d200aecbdb383eb122d5f45ce715d2f468583785d36fbfe5076ce033abb02486dc13b407171721cda6f474ed3f3a5a8e8c3d91367fa5f51ee374
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hoist-non-react-statics@npm:^3.3.2":
|
||||
version: 3.3.2
|
||||
resolution: "hoist-non-react-statics@npm:3.3.2"
|
||||
@@ -8780,6 +8810,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lowlight@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "lowlight@npm:3.1.0"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
devlop: "npm:^1.0.0"
|
||||
highlight.js: "npm:~11.9.0"
|
||||
checksum: 10c0/ee230ba1da3b339bae640479a09a4c82e5727bae38345421767c6407db4d514c10387300900ba79aa8c64dd79ae7f8d1acff847c01d5b0a20364a5ce04685f27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
|
||||
version: 10.4.3
|
||||
resolution: "lru-cache@npm:10.4.3"
|
||||
@@ -11086,6 +11127,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rehype-highlight@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "rehype-highlight@npm:7.0.0"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
hast-util-to-text: "npm:^4.0.0"
|
||||
lowlight: "npm:^3.0.0"
|
||||
unist-util-visit: "npm:^5.0.0"
|
||||
vfile: "npm:^6.0.0"
|
||||
checksum: 10c0/bf9eba61ac2635db6c6635d3485456f2d6bdf43e3acba34deb673ddde82dc8e0a7a4ba81c4f26dda85ecc5e99a9e949c05ed1b4fb25c0414e970d9623894c935
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rehype-raw@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "rehype-raw@npm:7.0.0"
|
||||
@@ -11097,6 +11151,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rehype-sanitize@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "rehype-sanitize@npm:6.0.0"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
hast-util-sanitize: "npm:^5.0.0"
|
||||
checksum: 10c0/43d6c056e63c994cf56e5ee0e157052d2030dc5ac160845ee494af9a26e5906bf5ec5af56c7d90c99f9c4dc0091e45a48a168618135fb6c64a76481ad3c449e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rehype-slug@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "rehype-slug@npm:6.0.0"
|
||||
@@ -12180,7 +12244,9 @@ __metadata:
|
||||
react-frame-component: "npm:^5.2.7"
|
||||
react-gauge-chart: "npm:^0.5.1"
|
||||
react-markdown: "npm:^9.0.1"
|
||||
rehype-highlight: "npm:^7.0.0"
|
||||
rehype-raw: "npm:^7.0.0"
|
||||
rehype-sanitize: "npm:^6.0.0"
|
||||
rehype-slug: "npm:^6.0.0"
|
||||
remark-flexible-toc: "npm:^1.1.1"
|
||||
remark-gfm: "npm:^4.0.0"
|
||||
@@ -12655,6 +12721,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-find-after@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "unist-util-find-after@npm:5.0.0"
|
||||
dependencies:
|
||||
"@types/unist": "npm:^3.0.0"
|
||||
unist-util-is: "npm:^6.0.0"
|
||||
checksum: 10c0/a7cea473c4384df8de867c456b797ff1221b20f822e1af673ff5812ed505358b36f47f3b084ac14c3622cb879ed833b71b288e8aa71025352a2aab4c2925a6eb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-is@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "unist-util-is@npm:6.0.0"
|
||||
|
||||
Reference in New Issue
Block a user