Add syntax highlighting to the Markdown element (#356)

Also fixes the styling for the copy button
This commit is contained in:
Evan Simkowitz
2024-09-09 11:56:21 -07:00
committed by GitHub
parent 5190556c37
commit 7c45b3679f
7 changed files with 126 additions and 17 deletions
+8 -2
View File
@@ -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);
}
}
+9 -6
View File
@@ -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>
);
+3 -5
View File
@@ -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;
}
+24 -3
View File
@@ -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}
+3
View File
@@ -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;
+2
View File
@@ -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",
+77 -1
View File
@@ -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"