feat: built-in callout (#1016)

* feat: built-in callout

* update callout padding

* lock dep version
This commit is contained in:
Stephen Zhou 2023-10-06 12:14:28 +08:00 committed by GitHub
parent e011c8924a
commit 9ade4e847c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 62 deletions

View File

@ -57,6 +57,7 @@
"@lezer/highlight": "1.1.6",
"@mantine/core": "7.0.2",
"@mantine/dates": "7.0.2",
"@microflash/remark-callout-directives": "1.6.0",
"@monaco-editor/react": "4.5.2",
"@popperjs/core": "2.11.8",
"@prisma/client": "5.3.1",

View File

@ -86,6 +86,9 @@ dependencies:
'@mantine/dates':
specifier: 7.0.2
version: 7.0.2(@mantine/core@7.0.2)(@mantine/hooks@7.0.2)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0)
'@microflash/remark-callout-directives':
specifier: 1.6.0
version: 1.6.0
'@monaco-editor/react':
specifier: 4.5.2
version: 4.5.2(monaco-editor@0.43.0)(react-dom@18.2.0)(react@18.2.0)
@ -2368,6 +2371,17 @@ packages:
- supports-color
dev: false
/@microflash/remark-callout-directives@1.6.0:
resolution: {integrity: sha512-W2ovtw9JdSpO/gjQb3wlf3o5AW4h+w6mewXybsrtSvifb/30P7ahBELDmFSCQBuXiofMbN8xO0/0GqVYoZrXmQ==}
dependencies:
defu: 6.1.2
hastscript: 7.2.0
mdast-util-from-markdown: 1.3.1
unist-util-visit: 4.1.2
transitivePeerDependencies:
- supports-color
dev: false
/@monaco-editor/loader@1.3.3(monaco-editor@0.43.0):
resolution: {integrity: sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==}
peerDependencies:
@ -5492,6 +5506,10 @@ packages:
object-keys: 1.1.1
dev: true
/defu@6.1.2:
resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==}
dev: false
/delaunator@5.0.0:
resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==}
dependencies:

View File

@ -136,20 +136,94 @@
}
.prose .callout {
@apply border border-zinc-100 bg-zinc-50 text-accent rounded-xl p-5 md:p-8 font-normal;
@apply -mx-5 md:-mx-8 my-10;
--callout-color: hsl(207, 14%, 62%);
--callout-background: ;
--callout-border: hsl(205, 15%, 33%);
--callout-radius: 8px;
--callout-padding: 2ch;
border-width: 1px;
border-style: solid;
border-color: var(--callout-border);
border-radius: var(--callout-radius);
background: var(--callout-background);
margin-top: 0.8em;
margin-bottom: 0.8em;
}
.prose .callout code,
.prose .callout pre {
background-color: hsl(0, 0%, 100%, 0.1);
border: none;
}
.prose .callout-content {
padding-left: var(--callout-padding);
padding-right: var(--callout-padding);
}
.prose .callout-content:first-child,
.prose .callout-content:only-child {
margin-block-start: 0;
}
.prose .callout-content:last-child,
.prose .callout-content:only-child {
margin-block-end: 0;
}
.prose .callout-indicator {
display: flex;
align-items: center;
padding: var(--callout-padding) var(--callout-padding) 0;
}
.prose .callout-hint {
margin-inline-end: calc(var(--callout-padding) / 4);
color: var(--callout-color);
}
.prose .callout-title {
margin-inline-start: calc(var(--callout-padding) / 4);
color: var(--callout-color);
font-size: 0.9em;
font-weight: bold;
letter-spacing: 0.04em;
}
.prose .callout-note {
--callout-background: hsla(0, 0%, 0%, 0.05);
--callout-color: hsl(0, 0%, 43%);
--callout-border: hsl(0, 0%, 0%, 0.25);
}
:root.dark .prose .callout-note {
--callout-background: hsl(0, 0%, 100%, 0.05);
--callout-color: hsl(0, 0%, 43%);
--callout-border: hsl(0, 0%, 100%, 0.25);
}
.prose .callout-commend {
--callout-background: hsl(153, 47%, 49%, 0.05);
--callout-color: hsl(153, 62%, 54%);
--callout-border: hsl(153, 47%, 49%, 0.25);
}
.prose .callout-warn {
@apply border-orange-200 bg-orange-100 text-orange-700;
--callout-background: hsl(45, 100%, 55%, 0.05);
--callout-color: hsl(45, 97%, 66%);
--callout-border: hsl(45, 100%, 55%, 0.25);
}
.prose .callout-success {
@apply border-green-200 bg-green-100 text-green-700;
.prose .callout-deter {
--callout-background: hsl(353, 83%, 58%, 0.05);
--callout-color: hsl(341, 89%, 63%);
--callout-border: hsl(353, 83%, 58%, 0.25);
}
.prose .callout-danger {
@apply border-red-200 bg-red-100 text-red-700;
.prose .callout-assert {
--callout-background: rgba(47, 131, 157, 0.05);
--callout-color: hsl(201, 89%, 63%);
--callout-border: hsl(195, 61%, 56%, 0.25);
}
.prose .code-wrapper {

View File

@ -29,6 +29,9 @@ import remarkParse from "remark-parse"
import remarkRehype from "remark-rehype"
import { unified } from "unified"
// @ts-expect-error
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import AdvancedImage from "~/components/ui/AdvancedImage"
import { transformers } from "./embed-transformers"
@ -44,7 +47,6 @@ import { rehypeTable } from "./rehype-table"
import { rehypeVideo } from "./rehype-video"
import { rehypeWrapCode } from "./rehype-wrap-code"
import { rehypeExternalLink } from "./rehyper-external-link"
import { remarkCallout } from "./remark-callout"
import { remarkMermaid } from "./remark-mermaid"
import { remarkPangu } from "./remark-pangu"
import { remarkYoutube } from "./remark-youtube"
@ -121,9 +123,9 @@ export const renderPageContent = (
.use(remarkGfm, {
singleTilde: false,
})
.use(remarkCallout)
.use(remarkDirective)
.use(remarkDirectiveRehype)
.use(remarkCalloutDirectives)
.use(remarkYoutube)
.use(remarkMermaid)
.use(remarkMath, {

View File

@ -1,50 +0,0 @@
import type { Root } from "mdast"
import type { Plugin } from "unified"
import { visit } from "unist-util-visit"
const CALLOUT_RE = /^(TIP|WARN|SUCCESS|DANGER):(\n|$)/
export const remarkCallout: Plugin<[], Root> = () => (tree: Root) => {
visit(tree, { type: "blockquote" }, (node, index, parent) => {
if (node.type !== "blockquote") return
const firstChild = node.children[0]
if (firstChild?.type === "paragraph") {
const paragraphFirstChild = firstChild.children[0]
if (
paragraphFirstChild.type === "text" &&
CALLOUT_RE.test(paragraphFirstChild.value)
) {
const type = paragraphFirstChild.value.match(CALLOUT_RE)![1]
const newValue = paragraphFirstChild.value.replace(CALLOUT_RE, "")
// In the following case `newValue` will be empty empty
// We should remove the paragraph
// > TIP:
// >
// > some tip
if (!newValue) {
node.children.splice(0, 1)
}
paragraphFirstChild.value = newValue
const className = `callout callout-${type.toLowerCase()}`
node.data = {
...node.data,
hProperties: {
class: className,
},
}
}
}
})
}
export const allowedBlockquoteAttrs: Array<[string, ...string[]]> = [
[
"className",
"callout",
"callout-tip",
"callout-warn",
"callout-success",
"callout-danger",
],
]

View File

@ -1,7 +1,5 @@
import { defaultSchema } from "rehype-sanitize"
import { allowedBlockquoteAttrs } from "./remark-callout"
const scheme = {
...defaultSchema,
tagNames: [
@ -16,11 +14,12 @@ const scheme = {
"audio",
"source",
"mermaid",
"aside",
...["svg", "path", "circle"],
],
attributes: {
...defaultSchema.attributes,
"*": [...(defaultSchema.attributes?.["*"] || []), "className", "style"],
blockquote: allowedBlockquoteAttrs,
video: ["src", "controls", "loop", "muted", "autoPlay", "playsInline"],
audio: [
"src",
@ -35,6 +34,19 @@ const scheme = {
],
source: ["src", "type"],
iframe: ["src", "allowFullScreen", "frameborder", "allow"],
svg: [
"xmlns",
"width",
"height",
"viewBox",
"fill",
"stroke",
"strokeLineCap",
"strokeLineJoin",
"strokeWidth",
],
path: ["d", "fill"],
circle: ["cx", "cy", "r", "fill"],
},
}