diff --git a/ui/components/Markdown/NodesRenderer.tsx b/ui/components/Markdown/NodesRenderer.tsx new file mode 100644 index 0000000..d06ea78 --- /dev/null +++ b/ui/components/Markdown/NodesRenderer.tsx @@ -0,0 +1,46 @@ +import { isEqual } from "@guanghechen/equal"; +import { useStateValue } from "@guanghechen/react-viewmodel"; +import type { Node } from "@yozora/ast"; +import React from "react"; +import type { INodeRenderer, INodeRendererMap } from "./context"; +import { useNodeRendererContext } from "./context"; + +export interface INodesRendererProps { + /** + * Ast nodes. + */ + nodes?: Node[]; +} + +export const NodesRenderer: React.FC = props => { + const { nodes } = props; + const { viewmodel } = useNodeRendererContext(); + const rendererMap: Readonly = useStateValue(viewmodel.rendererMap$); + if (!Array.isArray(nodes) || nodes.length <= 0) return ; + return ; +}; + +interface IProps { + nodes: Node[]; + rendererMap: Readonly; +} + +class NodesRendererInner extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return !isEqual(props.nodes, nextProps.nodes) || props.rendererMap !== nextProps.rendererMap; + } + + public override render(): React.ReactElement { + const { nodes, rendererMap } = this.props; + return ( + + {nodes.map((node, index) => { + const key = `${node.type}-${index}`; + const Renderer: INodeRenderer = rendererMap[node.type] ?? rendererMap._fallback; + return ; + })} + + ); + } +} diff --git a/ui/components/Markdown/ReactMarkdown.tsx b/ui/components/Markdown/ReactMarkdown.tsx new file mode 100644 index 0000000..0704481 --- /dev/null +++ b/ui/components/Markdown/ReactMarkdown.tsx @@ -0,0 +1,134 @@ +import { css, cx } from "@emotion/css"; +import { Definition, Root } from "@yozora/ast"; +import { calcDefinitionMap } from "@yozora/ast-util"; +import React from "react"; +import { + INodeRendererContext, + INodeRendererMap, + IReactMarkdownThemeScheme, + NodeRendererContextType, + ReactMarkdownViewModel, + astClasses, +} from "./context"; +import { NodesRenderer } from "./NodesRenderer"; +import { parser } from "./parser"; +import { buildNodeRendererMap } from "./renderer"; + +export interface IMarkdownProps { + /** + * Text content of markdown. + */ + text: string | string[]; + /** + * Customized node renderer mpa. + */ + customizedRendererMap?: Readonly>; + /** + * React markdown theme scheme. + */ + themeScheme?: IReactMarkdownThemeScheme; + /** + * Preset Link / Image reference definitions. + */ + presetDefinitionMap?: Readonly>; + /** + * Prefer code wrap. + * + * !!! Since the lineno would be weird if the code is wrapped, + * !!! so the lineno will be hidden if the `preferCodeWrap` set to `true`. + */ + preferCodeWrap?: boolean; + /** + * Whether if show lineno for code block. + */ + showCodeLineno?: boolean; + /** + * Root css class of the component. + */ + className?: string; + /** + * Root css style. + */ + style?: React.CSSProperties; +} + +export const ReactMarkdown: React.FC = props => { + const { + presetDefinitionMap, + customizedRendererMap, + preferCodeWrap = false, + showCodeLineno = true, + text, + themeScheme = "lighten", + className, + style, + } = props; + const ast: Root = React.useMemo(() => { + const asts: Root[] = Array.isArray(text) ? text.map(t => parser.parse(t)) : [parser.parse(text)]; + if (asts.length === 0) { + return parser.parse(""); + } + const root: Root = asts[0]; + for (let i = 1; i < asts.length; ++i) { + root.children.push(...asts[i].children); + } + return root; + }, [text]); + const definitionMap: Record> = React.useMemo( + () => calcDefinitionMap(ast).definitionMap, + [ast], + ); + + const [viewmodel] = React.useState( + () => + new ReactMarkdownViewModel({ + definitionMap: { + ...presetDefinitionMap, + ...definitionMap, + }, + rendererMap: buildNodeRendererMap(customizedRendererMap), + preferCodeWrap, + showCodeLineno, + themeScheme, + }), + ); + const context = React.useMemo(() => ({ viewmodel }), [viewmodel]); + + const cls: string = cx(rootCls, themeScheme === "darken" && astClasses.rootDarken, className); + + React.useEffect(() => { + viewmodel.preferCodeWrap$.next(preferCodeWrap); + }, [viewmodel, preferCodeWrap]); + + React.useEffect(() => { + viewmodel.showCodeLineno$.next(showCodeLineno); + }, [viewmodel, showCodeLineno]); + + React.useEffect(() => { + viewmodel.themeScheme$.next(themeScheme); + }, [viewmodel, themeScheme]); + + return ( +
+ + + +
+ ); +}; + +const rootCls = cx( + astClasses.root, + css({ + wordBreak: "break-all", + userSelect: "unset", + [astClasses.listItem]: { + [`> ${astClasses.list}`]: { + marginLeft: "1.2em", + }, + }, + "> :last-child": { + marginBottom: 0, + }, + }), +); diff --git a/ui/components/Markdown/context/constant.ts b/ui/components/Markdown/context/constant.ts new file mode 100644 index 0000000..b7c755f --- /dev/null +++ b/ui/components/Markdown/context/constant.ts @@ -0,0 +1,66 @@ +import { css } from "@emotion/css"; + +export const astClasses = { + root: css({ + "--colorBgBlockquote": "none", + "--colorBgTableHead": "hsl(0deg, 0%, 94%)", + "--colorBgTableEvenRow": "hsl(0deg, 0%, 96%)", + "--colorBgTableOddRow": "hsl(0deg, 0%, 100%)", + "--colorBorderBlockquote": "hsl(210deg, 13%, 85%)", + "--colorBorderHeading": "hsl(0deg, 0%, 80%)", + "--colorBorderImage": "hsl(277deg, 19%, 47%)", + "--colorBorderTable": "hsl(220deg, 7%, 90%)", + "--colorBgCode": "#f5f7f9", + "--colorDelete": "hsl(210deg, 8%, 65%)", + "--colorHeading": "hsl(0deg, 0%, 25%)", + "--colorImageTitle": "hsl(0deg, 0%, 50%)", + "--colorInlineCode": "hsl(348deg, 60%, 47%)", + "--colorLink": "hsl(206deg, 53%, 47%)", + "--colorLinkActive": "hsl(206deg, 53%, 52%)", + "--colorLinkHover": "hsl(206deg, 53%, 52%)", + "--colorLinkVisited": "hsl(206deg, 53%, 47%)", + "--fontFamilyCode": "Consolas, 'Source Code Pro', 'Roboto Mono', monospace, sans-serif", + "--fontFamilyHeading": "Consolas, 'Source Code Pro', 'Roboto Mono', monospace, sans-serif", + }), + rootDarken: css({ + "&&": { + "--colorBgBlockquote": "none", + "--colorBgTableHead": "hsl(200deg, 10%, 16%)", + "--colorBgTableEvenRow": "hsl(200deg, 10%, 16%)", + "--colorBgTableOddRow": "hsl(0deg, 0%, 9%)", + "--colorBorderBlockquote": "hsl(207deg, 7%, 45%)", + "--colorBorderHeading": "hsla(0deg, 0%, 30%, 0.8)", + "--colorBorderImage": "hsl(290deg, 15%, 49%)", + "--colorBorderTable": "hsl(0deg, 0%, 50%)", + "--colorBgCode": "hsl(0deg, 0%, 12%)", + "--colorDelete": "hsl(220deg, 5%, 68%)", + "--colorHeading": "hsl(0deg, 0%, 65%)", + "--colorImageTitle": "hsl(0deg, 0%, 50%)", + "--colorInlineCode": "hsl(348deg, 70%, 52%)", + "--colorLink": "hsl(207deg, 53%, 50%)", + "--colorLinkActive": "hsl(207deg, 53%, 50%)", + "--colorLinkHover": "hsl(207deg, 53%, 50%)", + "--colorLinkVisited": "hsl(207deg, 53%, 50%)", + "--fontFamilyCode": "Consolas, 'Source Code Pro', 'Roboto Mono', monospace, sans-serif", + "--fontFamilyHeading": "Consolas, 'Source Code Pro', 'Roboto Mono', monospace, sans-serif", + }, + }), + blockquote: css({}), + break: css({}), + code: css({}), + delete: css({}), + emphasis: css({}), + heading: css({}), + image: css({}), + imageReference: css({}), + inlineCode: css({}), + link: css({}), + linkReference: css({}), + list: css({}), + listItem: css({}), + paragraph: css({}), + strong: css({}), + table: css({}), + text: css({}), + thematicBreak: css({}), +}; diff --git a/ui/components/Markdown/context/context.tsx b/ui/components/Markdown/context/context.tsx new file mode 100644 index 0000000..009f56f --- /dev/null +++ b/ui/components/Markdown/context/context.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import type { ReactMarkdownViewModel } from "./viewmodel"; + +export interface INodeRendererContext { + readonly viewmodel: ReactMarkdownViewModel; +} + +export const NodeRendererContextType = React.createContext( + null as unknown as INodeRendererContext, +); +NodeRendererContextType.displayName = "NodeRendererContextType"; + +export const useNodeRendererContext = (): INodeRendererContext => { + return React.useContext(NodeRendererContextType); +}; diff --git a/ui/components/Markdown/context/index.ts b/ui/components/Markdown/context/index.ts new file mode 100644 index 0000000..142d89d --- /dev/null +++ b/ui/components/Markdown/context/index.ts @@ -0,0 +1,4 @@ +export * from "./constant"; +export * from "./context"; +export * from "./types"; +export * from "./viewmodel"; diff --git a/ui/components/Markdown/context/types.ts b/ui/components/Markdown/context/types.ts new file mode 100644 index 0000000..2fbde16 --- /dev/null +++ b/ui/components/Markdown/context/types.ts @@ -0,0 +1,73 @@ +import type { + Definition, + Blockquote, + BlockquoteType, + Break, + BreakType, + Code, + CodeType, + DefinitionType, + Delete, + DeleteType, + Emphasis, + EmphasisType, + Heading, + HeadingType, + Image, + ImageReference, + ImageReferenceType, + ImageType, + InlineCode, + InlineCodeType, + Link, + LinkReference, + LinkReferenceType, + LinkType, + List, + ListItem, + ListItemType, + ListType, + Node, + Paragraph, + ParagraphType, + Strong, + StrongType, + Table, + TableType, + Text, + TextType, + ThematicBreak, + ThematicBreakType, +} from "@yozora/ast"; +import type React from "react"; + +// Renderer for markdown AST node. +export type INodeRenderer = React.ComponentType | React.FC; + +/** + * Renderer map. + */ +export interface INodeRendererMap { + [BlockquoteType]: INodeRenderer
; + [BreakType]: INodeRenderer; + [CodeType]: INodeRenderer; + [DefinitionType]: INodeRenderer; + [DeleteType]: INodeRenderer; + [EmphasisType]: INodeRenderer; + [HeadingType]: INodeRenderer; + [ImageType]: INodeRenderer; + [ImageReferenceType]: INodeRenderer; + [InlineCodeType]: INodeRenderer; + [LinkType]: INodeRenderer; + [LinkReferenceType]: INodeRenderer; + [ListType]: INodeRenderer; + [ListItemType]: INodeRenderer; + [ParagraphType]: INodeRenderer; + [StrongType]: INodeRenderer; + [TableType]: INodeRenderer; + [TextType]: INodeRenderer; + [ThematicBreakType]: INodeRenderer; + _fallback: INodeRenderer; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: INodeRenderer; +} diff --git a/ui/components/Markdown/context/viewmodel.ts b/ui/components/Markdown/context/viewmodel.ts new file mode 100644 index 0000000..c76fb6c --- /dev/null +++ b/ui/components/Markdown/context/viewmodel.ts @@ -0,0 +1,49 @@ +import { State, ViewModel } from "@guanghechen/viewmodel"; +import type { Definition } from "@yozora/ast"; +import type { INodeRendererMap } from "./types"; + +export type IReactMarkdownThemeScheme = "lighten" | "darken" | string; + +export interface IReactMarkdownViewModelProps { + /** + * Link / Image reference definitions. + */ + readonly definitionMap: Readonly>; + /** + * Prefer code wrap. + * + * !!! Since the lineno would be weird if the code is wrapped, + * !!! so the lineno will be hidden if the `preferCodeWrap` set to `true`. + */ + readonly preferCodeWrap: boolean; + /** + * Ast node renderer map. + */ + readonly rendererMap: Readonly; + /** + * Whether if show code lineno. + */ + readonly showCodeLineno: boolean; + /** + * React markdown theme scheme. + */ + readonly themeScheme: IReactMarkdownThemeScheme; +} + +export class ReactMarkdownViewModel extends ViewModel { + public readonly definitionMap$: State>>; + public readonly preferCodeWrap$ = new State(false); + public readonly rendererMap$: State>; + public readonly showCodeLineno$: State; + public readonly themeScheme$: State; + + constructor(props: IReactMarkdownViewModelProps) { + super(); + + const { definitionMap, rendererMap, showCodeLineno, themeScheme } = props; + this.definitionMap$ = new State(definitionMap); + this.rendererMap$ = new State(rendererMap); + this.showCodeLineno$ = new State(showCodeLineno); + this.themeScheme$ = new State(themeScheme); + } +} diff --git a/ui/components/Markdown/index.ts b/ui/components/Markdown/index.ts new file mode 100644 index 0000000..76ca56f --- /dev/null +++ b/ui/components/Markdown/index.ts @@ -0,0 +1,3 @@ +export * from "./ReactMarkdown"; +export * from "./context"; +export * from "./parser"; diff --git a/ui/components/Markdown/parser/index.ts b/ui/components/Markdown/parser/index.ts new file mode 100644 index 0000000..53aeb78 --- /dev/null +++ b/ui/components/Markdown/parser/index.ts @@ -0,0 +1,20 @@ +import { Paragraph, ParagraphType, Root, TextType } from "@yozora/ast"; +import { GfmExParser as Parser } from "@yozora/parser-gfm-ex"; + +export const parser = new Parser({ + defaultParseOptions: { + shouldReservePosition: false, + }, +}); + +export const hasHighlightContent = (content: string): boolean => { + const data: Root = parser.parse(content); + if (data.children.length === 0) return false; + for (const node of data.children) { + if (node.type !== ParagraphType) return true; + + const paragraph = node as Paragraph; + if (paragraph.children.some(v => v.type !== TextType)) return true; + } + return false; +}; diff --git a/ui/components/Markdown/renderer/blockquote.tsx b/ui/components/Markdown/renderer/blockquote.tsx new file mode 100644 index 0000000..edf253e --- /dev/null +++ b/ui/components/Markdown/renderer/blockquote.tsx @@ -0,0 +1,42 @@ +import { css, cx } from "@emotion/css"; +import type { Blockquote } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `blockquote`. + * + * @see https://www.npmjs.com/package/@yozora/ast#blockquote + * @see https://www.npmjs.com/package/@yozora/tokenizer-blockquote + */ +export class BlockquoteRenderer extends React.Component
{ + public override shouldComponentUpdate(nextProps: Readonly
): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes = this.props.children; + return ( +
+ +
+ ); + } +} + +const cls = cx( + astClasses.blockquote, + css({ + boxSizing: "border-box", + padding: "0.625em 1em", + borderLeft: "0.25em solid var(--colorBorderBlockquote)", + margin: "0px 0px 1.25em 0px", + background: "var(--colorBgBlockquote)", + boxShadow: "0 1px 2px 0 hsla(0deg, 0%, 0%, 0.1)", + "> :last-child": { + marginBottom: 0, + }, + }), +); diff --git a/ui/components/Markdown/renderer/break.tsx b/ui/components/Markdown/renderer/break.tsx new file mode 100644 index 0000000..2c3f937 --- /dev/null +++ b/ui/components/Markdown/renderer/break.tsx @@ -0,0 +1,27 @@ +import { css, cx } from "@emotion/css"; +import type { Break } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; + +/** + * Render `break`. + * + * @see https://www.npmjs.com/package/@yozora/ast#break + * @see https://www.npmjs.com/package/@yozora/tokenizer-break + */ +export class BreakRenderer extends React.Component { + public override shouldComponentUpdate(): boolean { + return false; + } + + public override render(): React.ReactElement { + return
; + } +} + +const cls = cx( + astClasses.break, + css({ + boxSizing: "border-box", + }), +); diff --git a/ui/components/Markdown/renderer/code.tsx b/ui/components/Markdown/renderer/code.tsx new file mode 100644 index 0000000..3883b3e --- /dev/null +++ b/ui/components/Markdown/renderer/code.tsx @@ -0,0 +1,32 @@ +import { useStateValue } from "@guanghechen/react-viewmodel"; +import type { Code } from "@yozora/ast"; +import { useNodeRendererContext, type INodeRenderer, IReactMarkdownThemeScheme } from "../context"; +import { CodeRendererInner } from "./inner/CodeRendererInner"; + +/** + * Render `code` + * + * @see https://www.npmjs.com/package/@yozora/ast#code + * @see https://www.npmjs.com/package/@yozora/tokenizer-indented-code + * @see https://www.npmjs.com/package/@yozora/tokenizer-fenced-code + */ +export const CodeRenderer: INodeRenderer = props => { + const { lang } = props; + const value: string = props.value.replace(/[\r\n]+$/, ""); // Remove trailing line endings. + + const { viewmodel } = useNodeRendererContext(); + const preferCodeWrap: boolean = useStateValue(viewmodel.preferCodeWrap$); + const showCodeLineno: boolean = useStateValue(viewmodel.showCodeLineno$); + const themeScheme: IReactMarkdownThemeScheme = useStateValue(viewmodel.themeScheme$); + const darken: boolean = themeScheme === "darken"; + + return ( + + ); +}; diff --git a/ui/components/Markdown/renderer/delete.tsx b/ui/components/Markdown/renderer/delete.tsx new file mode 100644 index 0000000..3bae1a7 --- /dev/null +++ b/ui/components/Markdown/renderer/delete.tsx @@ -0,0 +1,37 @@ +import { css, cx } from "@emotion/css"; +import type { Delete, Node } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `delete`. + * + * @see https://www.npmjs.com/package/@yozora/ast#delete + * @see https://www.npmjs.com/package/@yozora/tokenizer-delete + */ +export class DeleteRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes: Node[] = this.props.children; + return ( + + + + ); + } +} + +const cls = cx( + astClasses.delete, + css({ + marginRight: "4px", + color: "var(--colorDelete)", + fontStyle: "italic", + textDecoration: "line-through", + }), +); diff --git a/ui/components/Markdown/renderer/emphasis.tsx b/ui/components/Markdown/renderer/emphasis.tsx new file mode 100644 index 0000000..a8f1832 --- /dev/null +++ b/ui/components/Markdown/renderer/emphasis.tsx @@ -0,0 +1,35 @@ +import { css, cx } from "@emotion/css"; +import type { Emphasis, Node } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `emphasis`. + * + * @see https://www.npmjs.com/package/@yozora/ast#emphasis + * @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis + */ +export class EmphasisRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes: Node[] = this.props.children; + return ( + + + + ); + } +} + +const cls = cx( + astClasses.emphasis, + css({ + fontStyle: "italic", + margin: "0 6px 0 2px", + }), +); diff --git a/ui/components/Markdown/renderer/heading.tsx b/ui/components/Markdown/renderer/heading.tsx new file mode 100644 index 0000000..02a5f83 --- /dev/null +++ b/ui/components/Markdown/renderer/heading.tsx @@ -0,0 +1,135 @@ +import { css, cx } from "@emotion/css"; +import type { Heading } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +type IHeading = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + +interface IProps extends Heading { + linkIcon?: React.ReactNode; +} + +/** + * Render `heading` content. + * + * @see https://www.npmjs.com/package/@yozora/ast#heading + * @see https://www.npmjs.com/package/@yozora/tokenizer-heading + */ +export class HeadingRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return ( + props.depth !== nextProps.depth || + props.identifier !== nextProps.identifier || + props.children !== nextProps.children || + props.linkIcon !== nextProps.linkIcon + ); + } + + public override render(): React.ReactElement { + const { depth, identifier, children, linkIcon = "ΒΆ" } = this.props; + + const id = identifier == null ? undefined : encodeURIComponent(identifier); + const h: IHeading = ("h" + depth) as IHeading; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const H: any = h as keyof JSX.IntrinsicElements; + const cls = cx(astClasses.heading, classes.heading, classes[h]); + + return ( + +

+ +

+ {identifier && ( + + {linkIcon} + + )} +
+ ); + } +} + +const anchorCls = css({ + flex: "0 0 3rem", + paddingLeft: "0.5rem", + color: "var(--colorLink)", + opacity: 0, + transition: "color 0.2s ease-in-out, opacity 0.2s ease-in-out", + userSelect: "none", + textDecoration: "none", + "> svg": { + overflow: "hidden", + display: "inline-block", + verticalAlign: "middle", + fill: "currentColor", + }, +}); + +const classes = { + heading: css({ + display: "flex", + alignItems: "center", + justifyContent: "flex-start", + padding: "0px", + margin: "0px 0px 1.25em 0px", + marginBottom: "1em", + lineHeight: "1.25", + fontFamily: "var(--fontFamilyHeading)", + color: "var(--colorHeading)", + [`&:active .${anchorCls}`]: { + opacity: 0.8, + color: "var(--colorLinkActive)", + }, + [`&&:hover .${anchorCls}`]: { + opacity: 0.8, + color: "var(--colorLinkHover)", + }, + }), + anchor: anchorCls, + content: css({ + flex: "0 1 auto", + minWidth: 0, + margin: 0, + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "pre-wrap", + lineHeight: "1.7", + }), + h1: css({ + padding: "0.3rem 0", + borderBottom: "1px solid var(--colorBorderHeading)", + fontSize: "2rem", + fontStyle: "normal", + fontWeight: 500, + }), + h2: css({ + padding: "0.3rem 0", + borderBottom: "1px solid var(--colorBorderHeading)", + fontSize: "1.5rem", + fontStyle: "normal", + fontWeight: 500, + marginBottom: "0.875rem", + }), + h3: css({ + fontSize: "1.25rem", + fontStyle: "normal", + fontWeight: 500, + }), + h4: css({ + fontSize: "1rem", + fontStyle: "normal", + fontWeight: 500, + }), + h5: css({ + fontSize: "0.875rem", + fontStyle: "normal", + fontWeight: 500, + }), + h6: css({ + fontSize: "0.85rem", + fontStyle: "normal", + fontWeight: 500, + }), +}; diff --git a/ui/components/Markdown/renderer/image.tsx b/ui/components/Markdown/renderer/image.tsx new file mode 100644 index 0000000..e67cd68 --- /dev/null +++ b/ui/components/Markdown/renderer/image.tsx @@ -0,0 +1,34 @@ +import type { Image } from "@yozora/ast"; +import React from "react"; +import type { INodeRenderer } from "../context"; +import { astClasses } from "../context"; +import { ImageRendererInner } from "./inner/ImageRendererInner"; + +/** + * Render `image`. + * + * @see https://www.npmjs.com/package/@yozora/ast#image + * @see https://www.npmjs.com/package/@yozora/tokenizer-image + */ +export const ImageRenderer: INodeRenderer = props => { + const { + url: src, + alt, + title, + srcSet, + sizes, + loading, + } = props as Image & React.ImgHTMLAttributes; + + return ( + + ); +}; diff --git a/ui/components/Markdown/renderer/imageReference.tsx b/ui/components/Markdown/renderer/imageReference.tsx new file mode 100644 index 0000000..118d871 --- /dev/null +++ b/ui/components/Markdown/renderer/imageReference.tsx @@ -0,0 +1,36 @@ +import { useStateValue } from "@guanghechen/react-viewmodel"; +import type { Definition, ImageReference } from "@yozora/ast"; +import React from "react"; +import { useNodeRendererContext, type INodeRenderer, astClasses } from "../context"; +import { ImageRendererInner } from "./inner/ImageRendererInner"; + +/** + * Render `imageReference`. + * + * @see https://www.npmjs.com/package/@yozora/ast#imageReference + * @see https://www.npmjs.com/package/@yozora/tokenizer-image-reference + */ +export const ImageReferenceRenderer: INodeRenderer = props => { + const { viewmodel } = useNodeRendererContext(); + const definitionMap: Readonly> = useStateValue( + viewmodel.definitionMap$, + ); + const { alt, srcSet, sizes, loading } = props as ImageReference & + React.ImgHTMLAttributes; + + const definition = definitionMap[props.identifier]; + const src: string = definition?.url ?? ""; + const title: string | undefined = definition?.title; + + return ( + + ); +}; diff --git a/ui/components/Markdown/renderer/index.ts b/ui/components/Markdown/renderer/index.ts new file mode 100644 index 0000000..6e1bcd6 --- /dev/null +++ b/ui/components/Markdown/renderer/index.ts @@ -0,0 +1,87 @@ +import { + BlockquoteType, + BreakType, + CodeType, + DefinitionType, + DeleteType, + EmphasisType, + HeadingType, + HtmlType, + ImageReferenceType, + ImageType, + InlineCodeType, + LinkReferenceType, + LinkType, + ListItemType, + ListType, + ParagraphType, + StrongType, + TableType, + TextType, + ThematicBreakType, +} from "@yozora/ast"; +import { INodeRendererMap } from "../context"; +import { BlockquoteRenderer } from "./blockquote"; +import { BreakRenderer } from "./break"; +import { CodeRenderer } from "./code"; +import { DeleteRenderer } from "./delete"; +import { EmphasisRenderer } from "./emphasis"; +import { HeadingRenderer } from "./heading"; +import { ImageRenderer } from "./image"; +import { ImageReferenceRenderer } from "./imageReference"; +import { InlineCodeRenderer } from "./inlineCode"; +import { LinkRenderer } from "./link"; +import { LinkReferenceRenderer } from "./linkReference"; +import { ListRenderer } from "./list"; +import { ListItemRenderer } from "./listItem"; +import { ParagraphRenderer } from "./paragraph"; +import { StrongRenderer } from "./strong"; +import { TableRenderer } from "./table"; +import { TextRenderer } from "./text"; +import { ThematicBreakRenderer } from "./thematicBreak"; + +export function buildNodeRendererMap( + customizedRendererMap?: Readonly>, +): Readonly { + if (customizedRendererMap == null) return defaultNodeRendererMap; + + let hasChanged = false; + const result: INodeRendererMap = {} as unknown as INodeRendererMap; + for (const [key, val] of Object.entries(customizedRendererMap)) { + if (val && val !== defaultNodeRendererMap[key]) { + hasChanged = true; + result[key] = val; + } + } + + return hasChanged ? { ...defaultNodeRendererMap, ...result } : defaultNodeRendererMap; +} + +// Default ast renderer map. +export const defaultNodeRendererMap: Readonly = { + [BlockquoteType]: BlockquoteRenderer, + [BreakType]: BreakRenderer, + [CodeType]: CodeRenderer, + [DefinitionType]: () => null, + [DeleteType]: DeleteRenderer, + [EmphasisType]: EmphasisRenderer, + [HeadingType]: HeadingRenderer, + [HtmlType]: () => null, + [ImageType]: ImageRenderer, + [ImageReferenceType]: ImageReferenceRenderer, + [InlineCodeType]: InlineCodeRenderer, + [LinkType]: LinkRenderer, + [LinkReferenceType]: LinkReferenceRenderer, + [ListType]: ListRenderer, + [ListItemType]: ListItemRenderer, + [ParagraphType]: ParagraphRenderer, + [StrongType]: StrongRenderer, + [TableType]: TableRenderer, + [TextType]: TextRenderer, + [ThematicBreakType]: ThematicBreakRenderer, + _fallback: function ReactMarkdownNodeFallback(node, key) { + // eslint-disable-next-line no-console + console.warn(`Cannot find render for \`${node.type}\` type node with key \`${key}\`:`, node); + return null; + }, +}; diff --git a/ui/components/Markdown/renderer/inlineCode.tsx b/ui/components/Markdown/renderer/inlineCode.tsx new file mode 100644 index 0000000..5b159ed --- /dev/null +++ b/ui/components/Markdown/renderer/inlineCode.tsx @@ -0,0 +1,36 @@ +import { css, cx } from "@emotion/css"; +import type { InlineCode } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; + +/** + * Render `inline-code`. + * + * @see https://www.npmjs.com/package/@yozora/ast#inlinecode + * @see https://www.npmjs.com/package/@yozora/tokenizer-inline-code + */ +export class InlineCodeRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.value !== nextProps.value; + } + + public override render(): React.ReactElement { + return {this.props.value}; + } +} + +const cls = cx( + astClasses.inlineCode, + css({ + padding: "1px 4px", + borderRadius: "4px", + margin: 0, + background: "hsla(210deg, 15%, 60%, 0.15)", + lineHeight: "1.375", + color: "var(--colorInlineCode)", + fontFamily: "var(--fontFamilyCode)", + fontSize: "min(1rem, 18px)", + fontWeight: 500, + }), +); diff --git a/ui/components/Markdown/renderer/inner/CodeRendererInner.tsx b/ui/components/Markdown/renderer/inner/CodeRendererInner.tsx new file mode 100644 index 0000000..126fc0b --- /dev/null +++ b/ui/components/Markdown/renderer/inner/CodeRendererInner.tsx @@ -0,0 +1,69 @@ +import { css, cx } from "@emotion/css"; +import CodeHighlighter from "@yozora/react-code-highlighter"; +import React from "react"; +import { astClasses } from "../../context"; +import { CopyButton } from "./CopyButton"; + +interface IProps { + darken: boolean; + lang: string; + value: string; + preferCodeWrap: boolean; + showCodeLineno: boolean; + pendingText?: string; + copyingText?: string; + copiedText?: string; + failedText?: string; +} + +export class CodeRendererInner extends React.PureComponent { + public override render(): React.ReactElement { + const { calcContentForCopy } = this; + const { darken, lang, value, preferCodeWrap, showCodeLineno } = this.props; + + return ( + + +
+ +
+
+ ); + } + + protected calcContentForCopy = (): string => { + return this.props.value; + }; +} + +const copyBtnCls = css({ + position: "absolute", + right: "4px", + top: "4px", + display: "none", +}); + +const codeCls = cx( + astClasses.code, + css({ + position: "relative", + display: "block", + boxSizing: "border-box", + borderRadius: "4px", + margin: "0px 0px 1.25em 0px", + backgroundColor: "var(--colorBgCode)", + [`&:hover > .${copyBtnCls}`]: { + display: "inline-block", + }, + [`&&[data-wrap="true"] > div`]: { + whiteSpace: "pre-wrap", + wordBreak: "keep-all", + }, + }), +); diff --git a/ui/components/Markdown/renderer/inner/CopyButton.tsx b/ui/components/Markdown/renderer/inner/CopyButton.tsx new file mode 100644 index 0000000..75d1199 --- /dev/null +++ b/ui/components/Markdown/renderer/inner/CopyButton.tsx @@ -0,0 +1,69 @@ +import { css, cx } from "@emotion/css"; +import { Copy as CopyIcon, ClipboardPaste as CopiedIcon } from "lucide-react"; +import copy from "copy-to-clipboard"; +import React from "react"; + +export enum CopyStatus { + PENDING = 0, + COPYING = 1, + COPIED = 2, + FAILED = 3, +} + +export interface ICopyButtonProps { + delay?: number; + className?: string; + calcContentForCopy: () => string; +} + +export const CopyButton: React.FC = props => { + const { className, delay = 1500, calcContentForCopy } = props; + const [status, setStatus] = React.useState(CopyStatus.PENDING); + const disabled: boolean = status !== CopyStatus.PENDING; + + const onCopy = () => { + if (status === CopyStatus.PENDING) { + setStatus(CopyStatus.COPYING); + try { + const contentForCopy: string = calcContentForCopy(); + copy(contentForCopy); + setStatus(CopyStatus.COPIED); + } catch (_error) { + setStatus(CopyStatus.FAILED); + } + } + }; + + React.useEffect((): (() => void) | undefined => { + if (status === CopyStatus.COPIED || status === CopyStatus.FAILED) { + const timer = setTimeout(() => setStatus(CopyStatus.PENDING), delay); + return () => { + if (timer) { + clearTimeout(timer); + } + }; + } + + return undefined; + }, [status, delay]); + + return ( + + ); +}; + +const classes = { + copyButton: css({ + cursor: "pointer", + }), +}; diff --git a/ui/components/Markdown/renderer/inner/ImageRendererInner.tsx b/ui/components/Markdown/renderer/inner/ImageRendererInner.tsx new file mode 100644 index 0000000..60495e3 --- /dev/null +++ b/ui/components/Markdown/renderer/inner/ImageRendererInner.tsx @@ -0,0 +1,59 @@ +import { css } from "@emotion/css"; +import React from "react"; + +interface IProps { + src: string; + alt: string; + title: string | undefined; + srcSet: string | undefined; + sizes: string | undefined; + loading: "eager" | "lazy" | undefined; + className: string; +} + +export class ImageRendererInner extends React.Component { + public override shouldComponentUpdate(nextProps: IProps): boolean { + const props = this.props; + return ( + props.src !== nextProps.src || + props.alt !== nextProps.alt || + props.title !== nextProps.title || + props.srcSet !== nextProps.srcSet || + props.sizes !== nextProps.sizes || + props.loading !== nextProps.loading || + props.className !== nextProps.className + ); + } + + public override render(): React.ReactElement { + const { src, alt, title, srcSet, sizes, loading, className } = this.props; + return ( +
+ {alt} + {title &&
{title}
} +
+ ); + } +} + +const cls = css({ + boxSizing: "border-box", + maxWidth: "80%", // Prevent images from overflowing the container. + display: "flex", + flexDirection: "column", + alignItems: "center", + margin: 0, + "> img": { + flex: "1 0 auto", + boxSizing: "border-box", + maxWidth: "100%", + border: "1px solid var(--colorBorderImage)", + boxShadow: "0 0 20px 1px rgba(126, 125, 150, 0.6)", + }, + "> figcaption": { + textAlign: "center", + fontStyle: "italic", + fontSize: "1em", + color: "var(--colorImageTitle)", + }, +}); diff --git a/ui/components/Markdown/renderer/inner/LinkRendererInner.tsx b/ui/components/Markdown/renderer/inner/LinkRendererInner.tsx new file mode 100644 index 0000000..0fc6df9 --- /dev/null +++ b/ui/components/Markdown/renderer/inner/LinkRendererInner.tsx @@ -0,0 +1,48 @@ +import { css, cx } from "@emotion/css"; +import type { Node } from "@yozora/ast"; +import React from "react"; +import { NodesRenderer } from "../../NodesRenderer"; + +interface IProps { + url: string; + title: string | undefined; + childNodes: Node[] | undefined; + className: string; +} + +export class LinkRendererInner extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return ( + props.url !== nextProps.url || + props.title !== nextProps.title || + props.childNodes !== nextProps.childNodes || + props.className !== nextProps.className + ); + } + + public override render(): React.ReactElement { + const { url, title, childNodes, className } = this.props; + return ( + + + + ); + } +} + +const cls = css({ + padding: "0.2rem 0", + color: "var(--colorLink)", + textDecoration: "none", + "&:active": { + color: "var(--colorLinkActive)", + }, + "&&:hover": { + color: "var(--colorLinkHover)", + textDecoration: "underline", + }, + "&:visited": { + color: "var(--colorLinkVisited)", + }, +}); diff --git a/ui/components/Markdown/renderer/link.tsx b/ui/components/Markdown/renderer/link.tsx new file mode 100644 index 0000000..ac913ab --- /dev/null +++ b/ui/components/Markdown/renderer/link.tsx @@ -0,0 +1,23 @@ +import type { Link } from "@yozora/ast"; +import { astClasses, type INodeRenderer } from "../context"; +import { LinkRendererInner } from "./inner/LinkRendererInner"; + +/** + * Render `link`. + * + * @see https://www.npmjs.com/package/@yozora/ast#link + * @see https://www.npmjs.com/package/@yozora/tokenizer-link + * @see https://www.npmjs.com/package/@yozora/tokenizer-autolink + * @see https://www.npmjs.com/package/@yozora/tokenizer-autolink-extension + */ +export const LinkRenderer: INodeRenderer = props => { + const { url, title, children: childNodes } = props; + return ( + + ); +}; diff --git a/ui/components/Markdown/renderer/linkReference.tsx b/ui/components/Markdown/renderer/linkReference.tsx new file mode 100644 index 0000000..c38df7d --- /dev/null +++ b/ui/components/Markdown/renderer/linkReference.tsx @@ -0,0 +1,28 @@ +import { useStateValue } from "@guanghechen/react-viewmodel"; +import type { Definition, LinkReference } from "@yozora/ast"; +import { useNodeRendererContext, type INodeRenderer, astClasses } from "../context"; +import { LinkRendererInner } from "./inner/LinkRendererInner"; + +/** + * Render `link-reference`. + * + * @see https://www.npmjs.com/package/@yozora/ast#linkReference + * @see https://www.npmjs.com/package/@yozora/tokenizer-link-reference + */ +export const LinkReferenceRenderer: INodeRenderer = props => { + const { viewmodel } = useNodeRendererContext(); + const definitionMap: Readonly> = useStateValue( + viewmodel.definitionMap$, + ); + const definition = definitionMap[props.identifier]; + const url: string = definition?.url ?? ""; + const title: string | undefined = definition?.title; + return ( + + ); +}; diff --git a/ui/components/Markdown/renderer/list.tsx b/ui/components/Markdown/renderer/list.tsx new file mode 100644 index 0000000..d4e39af --- /dev/null +++ b/ui/components/Markdown/renderer/list.tsx @@ -0,0 +1,53 @@ +import { css, cx } from "@emotion/css"; +import type { List } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `list`. + * + * @see https://www.npmjs.com/package/@yozora/ast#list + * @see https://www.npmjs.com/package/@yozora/tokenizer-list + */ +export class ListRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return ( + props.ordered !== nextProps.ordered || + props.orderType !== nextProps.orderType || + props.start !== nextProps.start || + props.children !== nextProps.children + ); + } + + public override render(): React.ReactElement { + const { ordered, orderType, start, children } = this.props; + + if (ordered) { + return ( +
    + +
+ ); + } + + return ( +
    + +
+ ); + } +} + +const cls = cx( + astClasses.list, + css({ + padding: "0px", + margin: "0 0 1em 2em", + lineHeight: "2", + "> :last-child": { + marginBottom: "0px", + }, + }), +); diff --git a/ui/components/Markdown/renderer/listItem.tsx b/ui/components/Markdown/renderer/listItem.tsx new file mode 100644 index 0000000..7e87c59 --- /dev/null +++ b/ui/components/Markdown/renderer/listItem.tsx @@ -0,0 +1,39 @@ +import { css, cx } from "@emotion/css"; +import type { ListItem, Node } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `listItem`. + * + * @see https://www.npmjs.com/package/@yozora/ast#listitem + * @see https://www.npmjs.com/package/@yozora/tokenizer-list-item + */ +export class ListItemRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes: Node[] = this.props.children; + return ( +
  • + +
  • + ); + } +} + +const cls = cx( + astClasses.listItem, + css({ + position: "relative", + padding: 0, + margin: 0, + "> :last-child": { + marginBottom: 0, + }, + }), +); diff --git a/ui/components/Markdown/renderer/paragraph.tsx b/ui/components/Markdown/renderer/paragraph.tsx new file mode 100644 index 0000000..89f9079 --- /dev/null +++ b/ui/components/Markdown/renderer/paragraph.tsx @@ -0,0 +1,72 @@ +import { css, cx } from "@emotion/css"; +import type { Node, Paragraph } from "@yozora/ast"; +import { ImageReferenceType, ImageType } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `paragraph`. + * + * @see https://www.npmjs.com/package/@yozora/ast#paragraph + * @see https://www.npmjs.com/package/@yozora/tokenizer-paragraph + */ +export class ParagraphRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes: Node[] = this.props.children; + + // If there are some image / imageReferences element in the paragraph, + // then wrapper the content with div to avoid the warnings such as: + // + // validateDOMNesting(...):
    cannot appear as a descendant of

    . + const notValidParagraph: boolean = childNodes.some( + child => child.type === ImageType || child.type === ImageReferenceType, + ); + + if (notValidParagraph) { + return ( +

    + +
    + ); + } + + return ( +

    + +

    + ); + } +} + +const paragraphCls: string = cx( + astClasses.paragraph, + css({ + overflow: "hidden", + padding: 0, + margin: "0px 0px 1.25em 0px", + marginBottom: "1em", + lineHeight: "1.8", + hyphens: "auto", + wordBreak: "normal", + overflowWrap: "anywhere", + "> :last-child": { + marginBottom: 0, + }, + }), +); +const paragraphDisplayCls: string = cx( + paragraphCls, + css({ + display: "flex", + alignItems: "center", + justifyContent: "center", + padding: "1rem 0", + margin: 0, + }), +); diff --git a/ui/components/Markdown/renderer/strong.tsx b/ui/components/Markdown/renderer/strong.tsx new file mode 100644 index 0000000..c033330 --- /dev/null +++ b/ui/components/Markdown/renderer/strong.tsx @@ -0,0 +1,34 @@ +import { css, cx } from "@emotion/css"; +import type { Node, Strong } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render `strong`. + * + * @see https://www.npmjs.com/package/@yozora/ast#strong + * @see https://www.npmjs.com/package/@yozora/tokenizer-emphasis + */ +export class StrongRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const childNodes: Node[] = this.props.children; + return ( + + + + ); + } +} + +const cls = cx( + astClasses.strong, + css({ + fontWeight: 600, + }), +); diff --git a/ui/components/Markdown/renderer/table.tsx b/ui/components/Markdown/renderer/table.tsx new file mode 100644 index 0000000..fb2ca54 --- /dev/null +++ b/ui/components/Markdown/renderer/table.tsx @@ -0,0 +1,145 @@ +import { css, cx } from "@emotion/css"; +import { isEqual } from "@guanghechen/equal"; +import type { Table } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; +import { NodesRenderer } from "../NodesRenderer"; + +/** + * Render yozora `table`, `tableRow` and `tableCell`. + * + * @see https://www.npmjs.com/package/@yozora/ast#table + * @see https://www.npmjs.com/package/@yozora/ast#tablecell + * @see https://www.npmjs.com/package/@yozora/ast#tablerow + * @see https://www.npmjs.com/package/@yozora/tokenizer-table + * @see https://www.npmjs.com/package/@yozora/tokenizer-table-row + * @see https://www.npmjs.com/package/@yozora/tokenizer-table-cell + */ +export class TableRenderer extends React.Component
    { + public override shouldComponentUpdate(nextProps: Readonly
    ): boolean { + const props = this.props; + return !isEqual(props.columns, nextProps.columns) || !isEqual(props.children, nextProps.children); + } + + public override render(): React.ReactElement { + const { columns, children: rows } = this.props; + const aligns = columns.map(col => col.align ?? undefined); + const [ths, ...tds] = rows.map(row => + row.children.map((cell, idx) => ), + ); + return ( +
    + + + {ths.map((children, idx) => ( + + ))} + + + + {tds.map((row, rowIndex) => ( + + {row.map((children, idx) => ( + + ))} + + ))} + +
    + {children} +
    + {children} +
    + ); + } +} + +interface IThProps { + align: "left" | "center" | "right" | undefined; + children: React.ReactNode; +} + +class Th extends React.Component { + protected readonly ref: React.RefObject; + + constructor(props: IThProps) { + super(props); + this.ref = { current: null }; + } + + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.align !== nextProps.align || props.children !== nextProps.children; + } + + public override render(): React.ReactElement { + const { align, children } = this.props; + return ( + + {children} + + ); + } + + public override componentDidMount(): void { + const th = this.ref.current; + if (th) { + th.setAttribute("title", th.innerText); + } + } + + public override componentDidUpdate(): void { + const th = this.ref.current; + if (th) { + th.setAttribute("title", th.innerText); + } + } +} + +const cls: string = cx( + astClasses.table, + css({ + display: "block", + overflow: "auto", + width: "max-content", + maxWidth: "100%", + padding: 0, + borderCollapse: "collapse", + borderRadius: "6px", + borderSpacing: "0px", + border: "1px solid var(--colorBorderTable)", + margin: "0 auto 1.25em", + lineHeight: "1.6", + "> thead": { + backgroundColor: "var(--colorBgTableHead)", + borderBottom: "1px solid #f0f0f0", + th: { + padding: "0.5rem 1rem", + borderLeft: "1px solid var(--colorBorderTable)", + wordBreak: "normal", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + "&:first-child": { + borderLeft: "none", + }, + }, + }, + "> tbody": { + tr: { + borderTop: "1px solid var(--colorBorderTable)", + backgroundColor: "var(--colorBgTableOddRow)", + }, + "tr:nth-child(2n)": { + backgroundColor: "var(--colorBgTableEvenRow)", + }, + td: { + padding: "0.5rem 1rem", + borderLeft: "1px solid var(--colorBorderTable)", + "&:first-child": { + borderLeft: "none", + }, + }, + }, + }), +); diff --git a/ui/components/Markdown/renderer/text.tsx b/ui/components/Markdown/renderer/text.tsx new file mode 100644 index 0000000..5f7bfae --- /dev/null +++ b/ui/components/Markdown/renderer/text.tsx @@ -0,0 +1,19 @@ +import type { Text } from "@yozora/ast"; +import React from "react"; + +/** + * Render `text`. + * + * @see https://www.npmjs.com/package/@yozora/ast#text + * @see https://www.npmjs.com/package/@yozora/tokenizer-text + */ +export class TextRenderer extends React.Component { + public override shouldComponentUpdate(nextProps: Readonly): boolean { + const props = this.props; + return props.value !== nextProps.value; + } + + public override render(): React.ReactElement { + return {this.props.value}; + } +} diff --git a/ui/components/Markdown/renderer/thematicBreak.tsx b/ui/components/Markdown/renderer/thematicBreak.tsx new file mode 100644 index 0000000..ab7bcba --- /dev/null +++ b/ui/components/Markdown/renderer/thematicBreak.tsx @@ -0,0 +1,35 @@ +import { css, cx } from "@emotion/css"; +import type { ThematicBreak } from "@yozora/ast"; +import React from "react"; +import { astClasses } from "../context"; + +/** + * Render `thematicBreak`. + * + * @see https://www.npmjs.com/package/@yozora/ast#thematicBreak + * @see https://www.npmjs.com/package/@yozora/tokenizer-thematic-break + */ +export class ThematicBreakRenderer extends React.Component { + public override shouldComponentUpdate(): boolean { + return false; + } + + public override render(): React.ReactElement { + return
    ; + } +} + +const cls = cx( + astClasses.thematicBreak, + css({ + boxSizing: "content-box", + display: "block", + height: 0, + width: "100%", + padding: 0, + border: 0, + borderBottom: `1px solid #dadada`, + outline: 0, + margin: "1.5em 0px", + }), +); diff --git a/ui/package.json b/ui/package.json index 8d54982..0118e05 100644 --- a/ui/package.json +++ b/ui/package.json @@ -11,17 +11,26 @@ "format:write": "prettier . --write" }, "dependencies": { + "@emotion/css": "11.11.2", + "@guanghechen/equal": "^1.0.0-alpha.6", + "@guanghechen/react-viewmodel": "^0.2.12", "@headlessui/react": "^1.7.18", "@icons-pack/react-simple-icons": "^9.4.0", "@langchain/openai": "^0.0.25", "@tailwindcss/typography": "^0.5.12", + "@yozora/ast": "^2.3.2", + "@yozora/ast-util": "^2.3.2", + "@yozora/parser-gfm-ex": "^2.3.2", + "@yozora/react-code-highlighter": "3.0.0-alpha.8", "clsx": "^2.1.0", + "copy-to-clipboard": "^3.2.0", "crypto-browserify": "^3.12.0", "langchain": "^0.1.30", "lucide-react": "^0.363.0", "markdown-to-jsx": "^7.4.5", "next": "14.1.4", "next-themes": "^0.3.0", + "prism-react-renderer": "^2.3.1", "process": "^0.11.10", "react": "^18", "react-dom": "^18", diff --git a/ui/yarn.lock b/ui/yarn.lock index 7d36732..6325493 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -27,6 +27,93 @@ node-fetch "^2.6.7" web-streams-polyfill "^3.2.1" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/generator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" + integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== + dependencies: + "@babel/types" "^7.24.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-environment-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9" + integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-function-name@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" + integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== + dependencies: + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-hoist-variables@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" + integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-module-imports@^7.16.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-split-export-declaration@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" + integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-string-parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2" + integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== + +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" + integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" + integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.0": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" @@ -34,6 +121,120 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/template@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" + integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/traverse@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5" + integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-hoist-variables" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" + integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== + dependencies: + "@babel/helper-string-parser" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" + integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + +"@emotion/css@11.11.2": + version "11.11.2" + resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.11.2.tgz#e5fa081d0c6e335352e1bc2b05953b61832dca5a" + integrity sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew== + dependencies: + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/serialize@^1.1.2": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.4.tgz#fc8f6d80c492cfa08801d544a05331d1cc7cd451" + integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" + integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== + +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -66,6 +267,55 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@guanghechen/disposable.types@^1.0.0-alpha.2": + version "1.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@guanghechen/disposable.types/-/disposable.types-1.0.0-alpha.2.tgz#723ebbf3957e11500293ed68550fc16b1c020a86" + integrity sha512-L7fnjbFoVIsz2w9haTlq4R4rn0gy5n1acIkQgI8DCjTwJgeyQ1ctcLrS8hU1whYhP8qw9Oyb6w8iLYJnja48Uw== + +"@guanghechen/disposable@^1.0.0-alpha.4": + version "1.0.0-alpha.4" + resolved "https://registry.yarnpkg.com/@guanghechen/disposable/-/disposable-1.0.0-alpha.4.tgz#13157c9079ab8b6f6a128b0c50a8e700d04f6761" + integrity sha512-Tki/RFrqB+Img28u/YkWKz1ZwlTCTtqlfiOOMa9Qk50p856zHGCeI1rqy2dqOxbSKqU2tVxDpg/0X+RXKK/qRQ== + dependencies: + "@guanghechen/disposable.types" "^1.0.0-alpha.2" + +"@guanghechen/equal@^1.0.0-alpha.1", "@guanghechen/equal@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.yarnpkg.com/@guanghechen/equal/-/equal-1.0.0-alpha.6.tgz#bfb2f4e5b7772c75cca781c208fdb4feed3c9796" + integrity sha512-++mkJjkSQfuDIa3YM6fbb2MC9job3H0H1GTGiQmkJZV2Ok6JqMcD7sG84Fb54lENywcd4wxqYFa2Wu1mfeWFFA== + +"@guanghechen/observable@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@guanghechen/observable/-/observable-6.1.1.tgz#5fc18e24a16cbffa355f889f2124773217699af9" + integrity sha512-jSab7Y3l29UvbIE4JLrFkCXJ+YHMNBG6r/tZb6CxmP6t36TAEzYLyMlbg9YTSOJxDhh+tQ40Zo1bm+7I1JLBNA== + dependencies: + "@guanghechen/disposable" "^1.0.0-alpha.4" + "@guanghechen/subscriber" "^1.0.0-alpha.1" + +"@guanghechen/react-viewmodel@^0.2.12": + version "0.2.12" + resolved "https://registry.yarnpkg.com/@guanghechen/react-viewmodel/-/react-viewmodel-0.2.12.tgz#8cb6cc34247c214f4e8de3a2b669d8d27a8da371" + integrity sha512-j/RTsT1MGru2hylk+q645pOWhUNA0P61Z3dNFpa4ueXdRb+F3Oy1Ea0UiUqfIU3yElKjbxeQ1E2MeUpqW3mw7w== + dependencies: + "@guanghechen/equal" "^1.0.0-alpha.6" + "@guanghechen/viewmodel" "^1.0.0-alpha.19" + +"@guanghechen/subscriber@^1.0.0-alpha.1": + version "1.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@guanghechen/subscriber/-/subscriber-1.0.0-alpha.1.tgz#48b97ada74e572d6969b225e3288d67b96fbb4a4" + integrity sha512-T5mNMKwRWx3rUYs7wzprEoj74qFFA6UTib/iZhC5Z4WSGhvrcxr8TgQMZ7+g78PotFwriAQJWELVQv5hkCuPyQ== + dependencies: + "@guanghechen/disposable" "^1.0.0-alpha.4" + +"@guanghechen/viewmodel@^1.0.0-alpha.19": + version "1.0.0-alpha.19" + resolved "https://registry.yarnpkg.com/@guanghechen/viewmodel/-/viewmodel-1.0.0-alpha.19.tgz#32e588fa5a8a65aeb350352fb93403b13cfd544c" + integrity sha512-uXG0Rdg70lEL68syihoeqP+XxbfqdOv6gELwINPgmPObd0conoStUlJXBChOUgBcH1IHqMx5um8gjsan4Fbz+w== + dependencies: + "@guanghechen/disposable" "^1.0.0-alpha.4" + "@guanghechen/observable" "^6.1.1" + "@guanghechen/subscriber" "^1.0.0-alpha.1" + "@headlessui/react@^1.7.18": version "1.7.18" resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7" @@ -110,7 +360,7 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" -"@jridgewell/gen-mapping@^0.3.2": +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== @@ -134,7 +384,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.24": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -339,6 +589,16 @@ dependencies: undici-types "~5.26.4" +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/prismjs@^1.26.0": + version "1.26.4" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.4.tgz#1a9e1074619ce1d7322669e5b46fbe823925103a" + integrity sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg== + "@types/prop-types@*": version "15.7.12" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" @@ -420,6 +680,317 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@yozora/ast-util@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/ast-util/-/ast-util-2.3.2.tgz#47b6d4e5c5a7e170aafe2f952af408af509d648a" + integrity sha512-mW8r/gbgIfACarWfgLzBMU6mSJLmLYdPgAvHttGVm7Ucd+zWwZLmJ3JeNbUvcvAOpTQkE7s2WNVZTbeuO5z2nA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + +"@yozora/ast@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/ast/-/ast-2.3.2.tgz#ed1691de67844d40c3f32987dc398d809043b403" + integrity sha512-YDA8TdWjufVYEqLwuONDLNwW5epBWlsNT2IQtats/Y8i4QOYYE56bAGaTVsGcq4TMF8qmkVUt9bsKuPYmHRJ3Q== + +"@yozora/character@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/character/-/character-2.3.2.tgz#d019fc024734dc7b1cc5dcdcc891efcec3fdc45b" + integrity sha512-jdzmFnMLKbFkCT/vVhQAk/kBY6Ot0ks84ws/En7pDRxiubXfrSGgkdID0SahfQCQbfqffo4tUit0O/Q3vQZPlw== + +"@yozora/core-parser@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/core-parser/-/core-parser-2.3.2.tgz#d607f60ca2eb55ac35f7724338607da803f55ed4" + integrity sha512-WVAjGa0UwKZctwqRJirUiznAZ9UoiPgSuKBhxqg10H6dV50+x0ScFAr9cQwC3mdT9UlHxG+0JaMnr0atyfsToA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/ast-util" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/invariant" "^2.3.2" + +"@yozora/core-react-constant@^3.0.0-alpha.6": + version "3.0.0-alpha.7" + resolved "https://registry.yarnpkg.com/@yozora/core-react-constant/-/core-react-constant-3.0.0-alpha.7.tgz#c15459b66e74e6ea2ba5815e5c566a3d85297a40" + integrity sha512-j64e0UBja0SeLci41V53IhEnAjPT/a/eKk3+H4cEi0YPI00JJaPBq2AxM5zGB9gzT2W24SOIzq3hs1+B9NIY9A== + +"@yozora/core-tokenizer@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/core-tokenizer/-/core-tokenizer-2.3.2.tgz#100cc2614b409bbf44a0d24cdfbd2feba3cba2ef" + integrity sha512-g7Xw0puubmet0UVMmX4mi+Q5/Pb4xkzlnqZF23sWOB2ug7MkTGR3xH0BBBNtQQs2gV8NdGuoyCMmG+bhsZIejQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + +"@yozora/invariant@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/invariant/-/invariant-2.3.2.tgz#c1202f6198f5653671b800f7b61c24c6d15134ad" + integrity sha512-gcqjGyWUi5JGO2/Kka35G6Kn2ecDhPYhSgLKP+aHmqL/tUnSxbK+AITJu7eDa9Ro1RVetRfXIG2XFZbjg2xH5w== + +"@yozora/parser-gfm-ex@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/parser-gfm-ex/-/parser-gfm-ex-2.3.2.tgz#ec33f7fa8f7e6bb3292fc706c8e420aafd520b7d" + integrity sha512-M1iDJiTNYOzOOwup3f0stJqDZ5XUnEBgX+ecWt1SLVWi7jJAyj2Ijay6s8w2YQhK/C5MsFNfdZJhMo1WcA00CQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/core-parser" "^2.3.2" + "@yozora/tokenizer-autolink" "^2.3.2" + "@yozora/tokenizer-autolink-extension" "^2.3.2" + "@yozora/tokenizer-blockquote" "^2.3.2" + "@yozora/tokenizer-break" "^2.3.2" + "@yozora/tokenizer-definition" "^2.3.2" + "@yozora/tokenizer-delete" "^2.3.2" + "@yozora/tokenizer-emphasis" "^2.3.2" + "@yozora/tokenizer-fenced-code" "^2.3.2" + "@yozora/tokenizer-heading" "^2.3.2" + "@yozora/tokenizer-html-block" "^2.3.2" + "@yozora/tokenizer-html-inline" "^2.3.2" + "@yozora/tokenizer-image" "^2.3.2" + "@yozora/tokenizer-image-reference" "^2.3.2" + "@yozora/tokenizer-indented-code" "^2.3.2" + "@yozora/tokenizer-inline-code" "^2.3.2" + "@yozora/tokenizer-link" "^2.3.2" + "@yozora/tokenizer-link-reference" "^2.3.2" + "@yozora/tokenizer-list" "^2.3.2" + "@yozora/tokenizer-paragraph" "^2.3.2" + "@yozora/tokenizer-setext-heading" "^2.3.2" + "@yozora/tokenizer-table" "^2.3.2" + "@yozora/tokenizer-text" "^2.3.2" + "@yozora/tokenizer-thematic-break" "^2.3.2" + +"@yozora/react-code-highlighter@3.0.0-alpha.8": + version "3.0.0-alpha.8" + resolved "https://registry.yarnpkg.com/@yozora/react-code-highlighter/-/react-code-highlighter-3.0.0-alpha.8.tgz#0e4184f37d8555fa5f01cb13a8a594f88b32ead9" + integrity sha512-SH10VNMWUUmXSpHYktWETQrRf2OtgHX2Gv1sDihSkwkCd2NPkHCUFPG7fl/WRST3kEpHP05F94TNk+nvRDsvKA== + dependencies: + "@guanghechen/equal" "^1.0.0-alpha.1" + "@yozora/core-react-constant" "^3.0.0-alpha.6" + prismjs "^1.29.0" + +"@yozora/tokenizer-autolink-extension@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-autolink-extension/-/tokenizer-autolink-extension-2.3.2.tgz#98c86a3f0c8c18181700882eca82c42494a8df8e" + integrity sha512-s4Q/wkO2nIMTkjcWH5SG+B8U0lLy0PhR8nzV00yBgrWTaTRcxpN16ecvS2Abrc+ezDnFmFB9pb1ZJ1jzturDyQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-autolink" "^2.3.2" + +"@yozora/tokenizer-autolink@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-autolink/-/tokenizer-autolink-2.3.2.tgz#e59c471336607d960054059a71737d5e0a3cbfd3" + integrity sha512-WQnMrKTN20KSp0OW375ofrkOw1CbIJi+eP1d5kgxx+aFhbHS0qS9zykQ8roz7Ovi6v3fBWDLxLS4J8s96VlPxA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-blockquote@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-blockquote/-/tokenizer-blockquote-2.3.2.tgz#419996c3bb786e117bdaaf86a17e453f438f5617" + integrity sha512-j9vCvVWJ+JVW16PhBRh5DXHO1X3Y2sro6zht1nAiWn722sM9tb6Ha7FTcLl0aTs5uu+Hguo3e1XiihVZF9Ud4Q== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-break@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-break/-/tokenizer-break-2.3.2.tgz#3dcb70df8515d1a0f69d507f3ca184aceba799f6" + integrity sha512-vvkgNGsrGC3ggdXjmcH13h28z/rGtC/siEeuNtrZt+1I6VeD54nbN19KSSYYI1gERDU3zPADqBcrPcNRvXe3/g== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-definition@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-definition/-/tokenizer-definition-2.3.2.tgz#771ff711d915ce6ad6d2b92ab419e0440bbb3ece" + integrity sha512-GxdSi6ykgDiSIhH95dv0K+kf2RLhYAtKb6df7n33Zy7aDQPEiUrLH+MWAME0WKKUmY7+sWlDLhtOWcbecFcZHw== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-delete@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-delete/-/tokenizer-delete-2.3.2.tgz#58b983b28b3d6c3f36570f6475cda4650234377c" + integrity sha512-lUgv6Vr0D0/+LdxHsK8nWta/WiHTGXQB2fhXyE2g8uGoOvp6FX5oNss1UcSXpr/wMvWgT/39YtcDvEeIJnO6wQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-emphasis@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-emphasis/-/tokenizer-emphasis-2.3.2.tgz#656c55de14dac594c303b29c865c1d17d9e6169c" + integrity sha512-RWfxj/rmglwKklpgTdz17MdoywsJs6RgHQn3gwJ21A2FL1VajbSEbMcV42BJPYFmzlpPU0ZNeeOIUu7QF3Z/hw== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-fenced-block@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-fenced-block/-/tokenizer-fenced-block-2.3.2.tgz#37efae62ce42121ae3d91a720853bf84bc25e8d0" + integrity sha512-YELd4nBRmpBw5X+oGt2z24w9V2yhm4ltXpukFkiBRPlPHin5K3G8KXlLkF+5KLGhr0duTmJEztgZ5Bxo7Qa9+w== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-fenced-code@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-fenced-code/-/tokenizer-fenced-code-2.3.2.tgz#ef5bc85fadb965c20e50fcf6efe3d15d7f56e0c5" + integrity sha512-17zokbGr7pCdIZSQdx2wChfsPa91bzBXTDvWUgrGXSCn+XTCfnUgFqlNidnVtc32mwRvsSxdtf6NGHHb4E7xtw== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-fenced-block" "^2.3.2" + +"@yozora/tokenizer-heading@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-heading/-/tokenizer-heading-2.3.2.tgz#9699c8d06515f182b80b74d08b5bdb64305b26a7" + integrity sha512-Vf0EFKYTKyZ2kdpMSjhbfbwjJrjCITVvCmWnfAhp3rUgICe30CUQuvsOFco2B9JHnpUdqc0wWHdUs4fvt6lTxQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-html-block@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-html-block/-/tokenizer-html-block-2.3.2.tgz#b707d95d1c9c721c079d6842787c881dd68d79c5" + integrity sha512-klLFjO0kRWwNZHLcmLJle8StIqAysstvgnYg3evYmd7fnVv8gnfROCseoOUJ0Jh1Fr0Uz3iQz4Xx0D11Q8uiWQ== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-html-inline@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-html-inline/-/tokenizer-html-inline-2.3.2.tgz#a1bd6dcb2ce9a98e4382155eca03eddc492e40f0" + integrity sha512-SbQlRQQ7cM6TUqiwdMJSZdsw5p59T8rpgbvzlrqcfzI1Jgo9zlw0pFWYjhDLowIdX68LldYfQLbqYKmlJLaxKA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-html-block" "^2.3.2" + +"@yozora/tokenizer-image-reference@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-image-reference/-/tokenizer-image-reference-2.3.2.tgz#518845bd3cc333d8491c977f2dd1e8998dc4b6ee" + integrity sha512-Suw9muo/eDpj+deKaoKSECDCzuf+Xr/10s0lm/GY/LYbJnOcnQtxD4nasF2W6fQcQJcbJcs7JAd3n3eI/0ZRFg== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-image" "^2.3.2" + "@yozora/tokenizer-link" "^2.3.2" + "@yozora/tokenizer-link-reference" "^2.3.2" + +"@yozora/tokenizer-image@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-image/-/tokenizer-image-2.3.2.tgz#67e59db0bfedbe09724718ae1cb1aab61db31226" + integrity sha512-2v4bkHeOoZYOuFCsmnoUY/4oHfoPsIWtaFuTSj3iuqYSgNJszm2ghgNESz1q8oQZPC6XL00ilGaEsaCeJuhd9w== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-link" "^2.3.2" + +"@yozora/tokenizer-indented-code@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-indented-code/-/tokenizer-indented-code-2.3.2.tgz#c7af8e7338c9ec378b6c75049936e7bf0d2086bf" + integrity sha512-iT8JBMPbghy/ITrU+Ms8stzfDFmVIGv3Kb04yVUiUrtIO1fQ0yMNWsCXwMQ5C4d+yW1DHKtMJf7RHOD5ppjWKA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-inline-code@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-inline-code/-/tokenizer-inline-code-2.3.2.tgz#f138f4a11d87f75e571c1973cfd7521ef90d6400" + integrity sha512-Pf5c7qVkOuRwGwLh1u6U+DIEH2J0SQooCrG2JnAS7RJCscKTqrPU+UNeALDqapThsF7SWFYVEQbBeTuLkarZlA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-link-reference@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-link-reference/-/tokenizer-link-reference-2.3.2.tgz#51762a2088052d11393f2416abf09bf1bffad708" + integrity sha512-sgHGYSapxodJRLwEJoHDVbqhLXWT5lLKSsMj1rVPUEmIfE/vl06OUOuMD+62csU7Kkj9IbbGmBu6zMPc4uuApw== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + "@yozora/tokenizer-link" "^2.3.2" + +"@yozora/tokenizer-link@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-link/-/tokenizer-link-2.3.2.tgz#696cfc9f3a1d9d066e3ce61d96d7c4864f7162d3" + integrity sha512-LlqZeB0O9/oUjHWUYW3wefDPXYu2C9NXQ8M1fWgCrc8whSN7vdh+/hU1AWKVF6mIiRXAnKM7o26WQk+0Fgc3uA== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-list@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-list/-/tokenizer-list-2.3.2.tgz#52f90ba7039945026a5d339ffa89fd331c0892dc" + integrity sha512-6KaOC84c7BCwJBIWGiGZUa2nT0xCPv0E7vfZyaxiReCeW+IMptxt0ZrrX+iWIFvwKig8x0X0nbJHf/gaxLYV+w== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-paragraph@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-paragraph/-/tokenizer-paragraph-2.3.2.tgz#2b844dad2357e6087164059dd532fc42aebc3eff" + integrity sha512-3l6iH71WeOwzVLZ+hujU9b2YoeMEfX8jD4GzVBo8dgwlg1xNrdLqdtfroFMuibB0Cs+bRQGbsYxXlzyyMi3vWg== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-setext-heading@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-setext-heading/-/tokenizer-setext-heading-2.3.2.tgz#88d4aebce2ae7cf9bb3d4bfb863fd839f748b75f" + integrity sha512-knI5oOoUQH7LoIHlqm3YjndAxNMx9okY82fGCZunpI2tSbZyLKqJmGomdmodzgEjPqv483ZtKt5T6keupRf23A== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-table@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-table/-/tokenizer-table-2.3.2.tgz#0b90dd83961a191e8f96ff60da2535c24ea651e7" + integrity sha512-pRcxpGEjMETi5KT33AnjcHN6oNkdXN1/2x63a9MZqUsweLpu4cRNB/ixwrfxCr9WJH6nEzmZGFKW0HOqwvbm7A== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-text@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-text/-/tokenizer-text-2.3.2.tgz#031faaa6a9d1c6bd4b1952c28388d823ca642429" + integrity sha512-s3zJ5bdebrdgZDtdoVAkUZ0LYOfA4nIbq3gyUMOZq1ygNDxjTzMcgGr6mzMw8OzYkrWQT2XyN17YWR+zsgUw4w== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + +"@yozora/tokenizer-thematic-break@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@yozora/tokenizer-thematic-break/-/tokenizer-thematic-break-2.3.2.tgz#ccb83c575718ae9b737c53debfff71073bf2bae8" + integrity sha512-taYZ9iMJ7f3c0QEEkskf+84T0Ri1m+rxnJquZxIYRDHybjgpmquqyYPF4jp9ZozFShw5mxzN03Qqs1ryMiXm/A== + dependencies: + "@yozora/ast" "^2.3.2" + "@yozora/character" "^2.3.2" + "@yozora/core-tokenizer" "^2.3.2" + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -464,6 +1035,13 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -665,6 +1243,15 @@ axobject-query@^3.2.1: dependencies: dequal "^2.0.3" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -835,6 +1422,15 @@ caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.300015 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz#b4d5f67ab0746a3b8b5b6d1f06e39c51beb39a9e" integrity sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg== +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -876,11 +1472,23 @@ client-only@0.0.1, client-only@^0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== +clsx@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + clsx@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -888,6 +1496,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -915,11 +1528,34 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +convert-source-map@^1.5.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +copy-to-clipboard@^3.2.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -1173,6 +1809,13 @@ enhanced-resolve@^5.12.0: graceful-fs "^4.2.4" tapable "^2.2.0" +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2: version "1.23.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" @@ -1294,6 +1937,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -1577,6 +2225,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -1751,6 +2404,11 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^13.19.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -1799,6 +2457,11 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -1929,6 +2592,11 @@ is-array-buffer@^3.0.4: call-bind "^1.0.2" get-intrinsic "^1.2.1" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + is-async-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" @@ -2155,7 +2823,7 @@ js-tiktoken@^1.0.7, js-tiktoken@^1.0.8: dependencies: base64-js "^1.5.1" -"js-tokens@^3.0.0 || ^4.0.0": +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -2167,11 +2835,21 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2728,6 +3406,16 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.7: pbkdf2 "^3.1.2" safe-buffer "^5.2.1" +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2877,6 +3565,19 @@ prettier@^3.2.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== +prism-react-renderer@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz#e59e5450052ede17488f6bc85de1553f584ff8d5" + integrity sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw== + dependencies: + "@types/prismjs" "^1.26.0" + clsx "^2.0.0" + +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -3041,7 +3742,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.1.7, resolve@^1.22.2, resolve@^1.22.4: +resolve@^1.1.7, resolve@^1.19.0, resolve@^1.22.2, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -3211,6 +3912,11 @@ source-map-js@^1.0.2, source-map-js@^1.2.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + stream-browserify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" @@ -3349,6 +4055,11 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + sucrase@^3.32.0: version "3.35.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" @@ -3362,6 +4073,13 @@ sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -3433,6 +4151,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3440,6 +4163,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -3696,6 +4424,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yaml@^2.2.1, yaml@^2.3.4: version "2.4.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed"