mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-08-03 10:14:43 +00:00
feat: switch to remark from markdown-it (big)
* replaces old mentions with avatar and display name * renders things directly through React * replaces most of the markdown hacks with custom AST components * adds a tooltip to codeblock "copy to clipboard"
This commit is contained in:
108
src/components/markdown/plugins/remarkRegexComponent.ts
Normal file
108
src/components/markdown/plugins/remarkRegexComponent.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { Handler } from "mdast-util-to-hast";
|
||||
import type { Plugin } from "unified";
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
/**
|
||||
* Props given to custom components
|
||||
*/
|
||||
export interface CustomComponentProps {
|
||||
type: string;
|
||||
match: string;
|
||||
arg1: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new custom component matched by a given RegExp
|
||||
* @param type hast node type
|
||||
* @param regex Regex to match (must have one capture group)
|
||||
* @returns Unified Plugin
|
||||
*/
|
||||
export function createComponent(
|
||||
type: string,
|
||||
regex: RegExp,
|
||||
validator?: (match: string) => boolean,
|
||||
): Plugin {
|
||||
/**
|
||||
* Plugin which transforms a given RegExp into a custom component with given name.
|
||||
*/
|
||||
return () => {
|
||||
return (tree) => {
|
||||
visit(
|
||||
tree,
|
||||
"text",
|
||||
(
|
||||
node: { value: string },
|
||||
index: number,
|
||||
parent: { children: any[] },
|
||||
) => {
|
||||
const result = [];
|
||||
let start = 0;
|
||||
|
||||
regex.lastIndex = 0;
|
||||
|
||||
let match = regex.exec(node.value);
|
||||
|
||||
while (match) {
|
||||
if (!validator || validator(match[1])) {
|
||||
const position = match.index;
|
||||
|
||||
if (start !== position) {
|
||||
result.push({
|
||||
type: "text",
|
||||
value: node.value.slice(start, position),
|
||||
});
|
||||
}
|
||||
|
||||
result.push({
|
||||
type,
|
||||
match: match[1],
|
||||
arg1: match[2],
|
||||
});
|
||||
start = position + match[0].length;
|
||||
}
|
||||
|
||||
match = regex.exec(node.value);
|
||||
}
|
||||
|
||||
if (
|
||||
result.length > 0 &&
|
||||
parent &&
|
||||
typeof index === "number"
|
||||
) {
|
||||
if (start < node.value.length) {
|
||||
result.push({
|
||||
type: "text",
|
||||
value: node.value.slice(start),
|
||||
});
|
||||
}
|
||||
|
||||
parent.children.splice(index, 1, ...result);
|
||||
return index + result.length;
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass-through a component as-is from remark to rehype
|
||||
* @param name Tag name
|
||||
* @returns Handler
|
||||
*/
|
||||
export const passThroughRehype: (name: string) => Handler =
|
||||
(name: string) => (h, node) =>
|
||||
h(node, name, node);
|
||||
|
||||
/**
|
||||
* Pass-through multiple components at once
|
||||
* @param keys Tags
|
||||
* @returns Handlers
|
||||
*/
|
||||
export const passThroughComponents = (...keys: string[]) => {
|
||||
const obj: Record<string, Handler> = {};
|
||||
for (const key of keys) {
|
||||
obj[key] = passThroughRehype(key);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
Reference in New Issue
Block a user