feat: support markdown
This commit is contained in:
parent
bd230ddd4f
commit
85d144a1e9
34 changed files with 2350 additions and 4 deletions
66
ui/components/Markdown/context/constant.ts
Normal file
66
ui/components/Markdown/context/constant.ts
Normal file
|
@ -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({}),
|
||||
};
|
15
ui/components/Markdown/context/context.tsx
Normal file
15
ui/components/Markdown/context/context.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from "react";
|
||||
import type { ReactMarkdownViewModel } from "./viewmodel";
|
||||
|
||||
export interface INodeRendererContext {
|
||||
readonly viewmodel: ReactMarkdownViewModel;
|
||||
}
|
||||
|
||||
export const NodeRendererContextType = React.createContext<INodeRendererContext>(
|
||||
null as unknown as INodeRendererContext,
|
||||
);
|
||||
NodeRendererContextType.displayName = "NodeRendererContextType";
|
||||
|
||||
export const useNodeRendererContext = (): INodeRendererContext => {
|
||||
return React.useContext(NodeRendererContextType);
|
||||
};
|
4
ui/components/Markdown/context/index.ts
Normal file
4
ui/components/Markdown/context/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export * from "./constant";
|
||||
export * from "./context";
|
||||
export * from "./types";
|
||||
export * from "./viewmodel";
|
73
ui/components/Markdown/context/types.ts
Normal file
73
ui/components/Markdown/context/types.ts
Normal file
|
@ -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<T extends Node = Node> = React.ComponentType<T> | React.FC<T>;
|
||||
|
||||
/**
|
||||
* Renderer map.
|
||||
*/
|
||||
export interface INodeRendererMap {
|
||||
[BlockquoteType]: INodeRenderer<Blockquote>;
|
||||
[BreakType]: INodeRenderer<Break>;
|
||||
[CodeType]: INodeRenderer<Code>;
|
||||
[DefinitionType]: INodeRenderer<Definition>;
|
||||
[DeleteType]: INodeRenderer<Delete>;
|
||||
[EmphasisType]: INodeRenderer<Emphasis>;
|
||||
[HeadingType]: INodeRenderer<Heading>;
|
||||
[ImageType]: INodeRenderer<Image>;
|
||||
[ImageReferenceType]: INodeRenderer<ImageReference>;
|
||||
[InlineCodeType]: INodeRenderer<InlineCode>;
|
||||
[LinkType]: INodeRenderer<Link>;
|
||||
[LinkReferenceType]: INodeRenderer<LinkReference>;
|
||||
[ListType]: INodeRenderer<List>;
|
||||
[ListItemType]: INodeRenderer<ListItem>;
|
||||
[ParagraphType]: INodeRenderer<Paragraph>;
|
||||
[StrongType]: INodeRenderer<Strong>;
|
||||
[TableType]: INodeRenderer<Table>;
|
||||
[TextType]: INodeRenderer<Text>;
|
||||
[ThematicBreakType]: INodeRenderer<ThematicBreak>;
|
||||
_fallback: INodeRenderer;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: INodeRenderer<Node & any>;
|
||||
}
|
49
ui/components/Markdown/context/viewmodel.ts
Normal file
49
ui/components/Markdown/context/viewmodel.ts
Normal file
|
@ -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<Record<string, Definition>>;
|
||||
/**
|
||||
* 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<INodeRendererMap>;
|
||||
/**
|
||||
* Whether if show code lineno.
|
||||
*/
|
||||
readonly showCodeLineno: boolean;
|
||||
/**
|
||||
* React markdown theme scheme.
|
||||
*/
|
||||
readonly themeScheme: IReactMarkdownThemeScheme;
|
||||
}
|
||||
|
||||
export class ReactMarkdownViewModel extends ViewModel {
|
||||
public readonly definitionMap$: State<Readonly<Record<string, Definition>>>;
|
||||
public readonly preferCodeWrap$ = new State<boolean>(false);
|
||||
public readonly rendererMap$: State<Readonly<INodeRendererMap>>;
|
||||
public readonly showCodeLineno$: State<boolean>;
|
||||
public readonly themeScheme$: State<IReactMarkdownThemeScheme>;
|
||||
|
||||
constructor(props: IReactMarkdownViewModelProps) {
|
||||
super();
|
||||
|
||||
const { definitionMap, rendererMap, showCodeLineno, themeScheme } = props;
|
||||
this.definitionMap$ = new State(definitionMap);
|
||||
this.rendererMap$ = new State(rendererMap);
|
||||
this.showCodeLineno$ = new State<boolean>(showCodeLineno);
|
||||
this.themeScheme$ = new State<IReactMarkdownThemeScheme>(themeScheme);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue