Download src/components/features/markdown/remark-github-alerts.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 3.17 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/markdown/remark-github-alerts.ts
- Command line
-
hf download hf://SaylorTwift/openhands/src/components/features/markdown/remark-github-alerts.ts
-
curl -L -o remark-github-alerts.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/markdown/remark-github-alerts.ts
3.17 kB
| import type { Plugin } from "unified"; | |
| import type { Root } from "mdast"; | |
| import { visit } from "unist-util-visit"; | |
| export const ALERT_TYPES = [ | |
| "note", | |
| "tip", | |
| "important", | |
| "warning", | |
| "caution", | |
| ] as const; | |
| export type AlertType = (typeof ALERT_TYPES)[number]; | |
| // GitHub's alert marker has the form `[!TYPE]` on the first line of a | |
| // blockquote, optionally followed by a newline before the body. We match | |
| // case-insensitively because GitHub does, and we swallow the trailing | |
| // `\r?\n` so the body content can start on the next visual line without | |
| // inheriting a leading break. | |
| const ALERT_REGEX = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*\r?\n?/i; | |
| /** | |
| * Remark plugin that recognises GitHub-style alert blockquotes: | |
| * | |
| * > [!WARNING] | |
| * > Body content | |
| * | |
| * When the first text node of a blockquote matches `[!TYPE]`, the marker | |
| * is stripped and `markdown-alert markdown-alert-<type>` classes are | |
| * attached to the blockquote via `hProperties`. A downstream renderer | |
| * (see `blockquote.tsx`) inspects those classes to render the styled | |
| * alert layout. Blockquotes that don't match the marker are left alone. | |
| * | |
| * The plugin tolerates running before OR after `remark-breaks`: | |
| * - Before: the first text node is `"[!WARNING]\nBody"`, the regex | |
| * consumes the marker plus its newline, leaving `"Body"`. | |
| * - After: the first text node is just `"[!WARNING]"`, followed by a | |
| * `break` node. We drop the now-empty text node and the leading | |
| * break so the body paragraph reads naturally. | |
| */ | |
| function toClassList(value: unknown): string[] { | |
| if (Array.isArray(value)) return value as string[]; | |
| if (typeof value === "string") return value.split(/\s+/).filter(Boolean); | |
| return []; | |
| } | |
| export const remarkGithubAlerts: Plugin<[], Root> = () => (tree) => { | |
| // Remark plugins mutate the AST in place by design — that's the visitor | |
| // pattern `unist-util-visit` is built around. Aliasing each node to a | |
| // local variable keeps the mutations out of `no-param-reassign`'s view | |
| // without changing semantics. | |
| visit(tree, "blockquote", (node) => { | |
| const block = node; | |
| const firstChild = block.children[0]; | |
| if (firstChild?.type !== "paragraph") return; | |
| const paragraph = firstChild; | |
| const firstText = paragraph.children[0]; | |
| if (firstText?.type !== "text") return; | |
| const match = firstText.value.match(ALERT_REGEX); | |
| if (!match) return; | |
| const type = match[1].toLowerCase() as AlertType; | |
| const text = firstText; | |
| text.value = text.value.replace(ALERT_REGEX, ""); | |
| if (text.value === "") { | |
| paragraph.children.shift(); | |
| if (paragraph.children[0]?.type === "break") { | |
| paragraph.children.shift(); | |
| } | |
| if (paragraph.children.length === 0) { | |
| block.children.shift(); | |
| } | |
| } | |
| block.data = block.data ?? {}; | |
| const existingHProps = (block.data.hProperties ?? {}) as Record< | |
| string, | |
| unknown | |
| >; | |
| const baseClasses = toClassList(existingHProps.className); | |
| block.data.hProperties = { | |
| ...existingHProps, | |
| className: [...baseClasses, "markdown-alert", `markdown-alert-${type}`], | |
| }; | |
| }); | |
| }; | |