From 34bb2bbc135a3799e002d1e2377ba95b1f1c170f Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Fri, 8 Jul 2022 14:24:48 +0100 Subject: [PATCH 1/5] 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" --- package.json | 18 +- src/components/common/Emoji.tsx | 4 +- src/components/markdown/Markdown.module.scss | 220 --- src/components/markdown/Markdown.tsx | 2 +- src/components/markdown/RemarkRenderer.tsx | 192 +++ src/components/markdown/Renderer.tsx | 290 ---- src/components/markdown/plugins/Codeblock.tsx | 78 + src/components/markdown/plugins/anchors.tsx | 34 + src/components/markdown/plugins/channels.tsx | 21 + src/components/markdown/plugins/emoji.tsx | 43 + src/components/markdown/plugins/mentions.tsx | 42 + .../markdown/plugins/remarkRegexComponent.ts | 108 ++ src/components/markdown/plugins/spoiler.tsx | 45 + src/components/markdown/plugins/timestamps.ts | 39 + src/controllers/modals/ModalController.tsx | 4 - src/lib/links.ts | 28 +- yarn.lock | 1270 ++++++++++++++++- 17 files changed, 1815 insertions(+), 623 deletions(-) delete mode 100644 src/components/markdown/Markdown.module.scss create mode 100644 src/components/markdown/RemarkRenderer.tsx delete mode 100644 src/components/markdown/Renderer.tsx create mode 100644 src/components/markdown/plugins/Codeblock.tsx create mode 100644 src/components/markdown/plugins/anchors.tsx create mode 100644 src/components/markdown/plugins/channels.tsx create mode 100644 src/components/markdown/plugins/emoji.tsx create mode 100644 src/components/markdown/plugins/mentions.tsx create mode 100644 src/components/markdown/plugins/remarkRegexComponent.ts create mode 100644 src/components/markdown/plugins/spoiler.tsx create mode 100644 src/components/markdown/plugins/timestamps.ts diff --git a/package.json b/package.json index 5fcf312b..77435e5e 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@hcaptcha/react-hcaptcha": "^0.3.6", "@insertish/vite-plugin-babel-macros": "^1.0.5", "@preact/preset-vite": "^2.0.0", - "@revoltchat/ui": "1.0.69", + "@revoltchat/ui": "1.0.70", "@rollup/plugin-replace": "^2.4.2", "@styled-icons/boxicons-logos": "^10.38.0", "@styled-icons/boxicons-regular": "^10.38.0", @@ -86,7 +86,6 @@ "@types/lodash": "^4", "@types/lodash.defaultsdeep": "^4.6.6", "@types/lodash.isequal": "^4.5.5", - "@types/markdown-it": "^12.0.2", "@types/node": "^15.12.4", "@types/preact-i18n": "^2.3.0", "@types/prismjs": "^1.16.5", @@ -116,8 +115,7 @@ "lodash.defaultsdeep": "^4.6.1", "lodash.isequal": "^4.5.0", "long": "^5.2.0", - "markdown-it": "^12.0.6", - "markdown-it-emoji": "^2.0.0", + "mdast-util-to-hast": "^12.1.2", "mediasoup-client": "npm:@insertish/mediasoup-client@3.6.36-esnext", "mobx": "^6.6.0", "mobx-react-lite": "3.4.0", @@ -135,7 +133,15 @@ "react-router-dom": "^5.2.0", "react-scroll": "^1.8.2", "react-virtuoso": "^2.12.0", - "revolt.js": "6.0.3", + "rehype-katex": "^6.0.2", + "rehype-prism": "^2.1.3", + "rehype-react": "^7.1.1", + "remark-breaks": "^3.0.2", + "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", + "remark-parse": "^10.0.1", + "remark-rehype": "^10.1.0", + "revolt.js": "6.0.5", "rimraf": "^3.0.2", "sass": "^1.35.1", "semver": "^7.3.7", @@ -147,6 +153,8 @@ "styled-components": "^5.3.0", "typescript": "^4.4.2", "ulid": "^2.3.0", + "unified": "^10.1.2", + "unist-util-visit": "^4.1.0", "use-resize-observer": "^7.0.0", "vite-plugin-pwa": "^0.11.13", "workbox-precaching": "^6.1.5" diff --git a/src/components/common/Emoji.tsx b/src/components/common/Emoji.tsx index 9be2e8fd..b20ca7ae 100644 --- a/src/components/common/Emoji.tsx +++ b/src/components/common/Emoji.tsx @@ -1,3 +1,5 @@ +import { emojiDictionary } from "../../assets/emojis"; + export type EmojiPack = "mutant" | "twemoji" | "noto" | "openmoji"; let EMOJI_PACK: EmojiPack = "mutant"; @@ -40,7 +42,7 @@ function toCodePoint(rune: string) { .join("-"); } -function parseEmoji(emoji: string) { +export function parseEmoji(emoji: string) { if (emoji.startsWith("custom:")) { return `https://dl.insrt.uk/projects/revolt/emotes/${emoji.substring( 7, diff --git a/src/components/markdown/Markdown.module.scss b/src/components/markdown/Markdown.module.scss deleted file mode 100644 index cff232a1..00000000 --- a/src/components/markdown/Markdown.module.scss +++ /dev/null @@ -1,220 +0,0 @@ -.markdown { - user-select: text; - - :global(.emoji) { - object-fit: contain; - - height: 1.25em; - width: 1.25em; - margin: 0 0.05em 0 0.1em; - vertical-align: -0.2em; - } - - &[data-large-emojis="true"] :global(.emoji) { - width: 3rem; - height: 3rem; - margin-bottom: 0; - margin-top: 1px; - margin-right: 2px; - vertical-align: -0.3em; - } - - p, - pre { - margin: 0; - } - - a { - text-decoration: none; - - &[data-type="mention"] { - padding: 0 6px; - flex-shrink: 0; - font-weight: 600; - display: inline-block; - background: var(--secondary-background); - border-radius: calc(var(--border-radius) * 2); - - &:hover { - text-decoration: none; - } - } - - &:hover { - text-decoration: underline; - } - } - - h1, - h2, - h3, - h4, - h5, - h6, - ul, - ol, - blockquote { - margin: 0; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - &:not(:first-child) { - margin-top: 12px; - } - } - - ul, - ol { - list-style-position: inside; - padding-left: 10px; - } - - blockquote { - margin: 2px 0; - padding: 2px 0; - background: var(--hover); - border-radius: var(--border-radius); - border-inline-start: 4px solid var(--tertiary-background); - - > * { - margin: 0 8px; - } - } - - pre { - padding: 1em; - overflow-x: scroll; - border-radius: var(--border-radius); - background: var(--block) !important; - } - - p > code { - padding: 1px 4px; - flex-shrink: 0; - } - - code { - color: white; - font-size: 90%; - background: var(--block); - border-radius: var(--border-radius); - font-family: var(--monospace-font), monospace; - border-radius: 3px; - -webkit-box-decoration-break: clone; - } - - input[type="checkbox"] { - margin-right: 4px; - pointer-events: none; - } - - table { - border-collapse: collapse; - - th, - td { - padding: 6px; - border: 1px solid var(--tertiary-foreground); - } - } - - :global(.katex-block) { - overflow-x: auto; - } - - :global(.spoiler) { - padding: 0 2px; - cursor: pointer; - user-select: none; - color: transparent; - background: #151515; - border-radius: var(--border-radius); - - > * { - opacity: 0; - pointer-events: none; - } - - &:global(.shown) { - cursor: auto; - user-select: all; - color: var(--foreground); - background: var(--secondary-background); - - > * { - opacity: 1; - pointer-events: unset; - } - } - } - - :global(.code) { - font-family: var(--monospace-font), monospace; - - :global(.lang) { - width: fit-content; - padding-bottom: 8px; - - div { - color: #111; - cursor: pointer; - padding: 2px 6px; - font-weight: 600; - user-select: none; - display: inline-block; - background: var(--accent); - - font-size: 10px; - text-transform: uppercase; - box-shadow: 0 2px #787676; - border-radius: calc(var(--border-radius) / 3); - - &:active { - transform: translateY(1px); - box-shadow: 0 1px #787676; - } - } - } - } - - input[type="checkbox"] { - width: 0; - opacity: 0; - pointer-events: none; - } - - label { - pointer-events: none; - } - - input[type="checkbox"] + label:before { - width: 12px; - height: 12px; - content: "a"; - font-size: 10px; - margin-right: 6px; - line-height: 12px; - background: white; - position: relative; - display: inline-block; - border-radius: var(--border-radius); - } - - input[type="checkbox"][checked="true"] + label:before { - content: "✓"; - align-items: center; - display: inline-flex; - justify-content: center; - background: var(--accent); - } - - input[type="checkbox"] + label { - line-height: 12px; - position: relative; - } -} diff --git a/src/components/markdown/Markdown.tsx b/src/components/markdown/Markdown.tsx index 986fbd2d..9ff974fb 100644 --- a/src/components/markdown/Markdown.tsx +++ b/src/components/markdown/Markdown.tsx @@ -1,6 +1,6 @@ import { Suspense, lazy } from "preact/compat"; -const Renderer = lazy(() => import("./Renderer")); +const Renderer = lazy(() => import("./RemarkRenderer")); export interface MarkdownProps { content?: string | null; diff --git a/src/components/markdown/RemarkRenderer.tsx b/src/components/markdown/RemarkRenderer.tsx new file mode 100644 index 00000000..06d1c3f1 --- /dev/null +++ b/src/components/markdown/RemarkRenderer.tsx @@ -0,0 +1,192 @@ +import "katex/dist/katex.min.css"; +import rehypeKatex from "rehype-katex"; +import rehypePrism from "rehype-prism"; +import rehypeReact from "rehype-react"; +import remarkBreaks from "remark-breaks"; +import remarkGfm from "remark-gfm"; +import remarkMath from "remark-math"; +import remarkParse from "remark-parse"; +import remarkRehype from "remark-rehype"; +import styled, { css } from "styled-components"; +import { unified } from "unified"; + +import { createElement } from "preact"; +import { memo } from "preact/compat"; +import { useEffect, useMemo, useState } from "preact/hooks"; + +import { MarkdownProps } from "./Markdown"; +import { RenderCodeblock } from "./plugins/Codeblock"; +import { RenderAnchor } from "./plugins/anchors"; +import { remarkChannels, RenderChannel } from "./plugins/channels"; +import { isOnlyEmoji, remarkEmoji, RenderEmoji } from "./plugins/emoji"; +import { remarkMention, RenderMention } from "./plugins/mentions"; +import { passThroughComponents } from "./plugins/remarkRegexComponent"; +import { remarkSpoiler, RenderSpoiler } from "./plugins/spoiler"; +import { remarkTimestamps, timestampHandler } from "./plugins/timestamps"; +import "./prism"; + +/** + * Null element + */ +const Null: React.FC = () => null; + +/** + * Custom Markdown components + */ +const components = { + emoji: RenderEmoji, + mention: RenderMention, + spoiler: RenderSpoiler, + channel: RenderChannel, + a: RenderAnchor, + p: styled.p` + margin: 0; + + > code { + padding: 1px 4px; + flex-shrink: 0; + } + `, + h1: styled.h1` + margin: 0.2em 0; + `, + h2: styled.h2` + margin: 0.2em 0; + `, + h3: styled.h3` + margin: 0.2em 0; + `, + h4: styled.h4` + margin: 0.2em 0; + `, + h5: styled.h5` + margin: 0.2em 0; + `, + h6: styled.h6` + margin: 0.2em 0; + `, + pre: RenderCodeblock, + code: styled.code` + color: white; + background: var(--block); + + font-size: 90%; + font-family: var(--monospace-font), monospace; + + border-radius: 3px; + box-decoration-break: clone; + `, + table: styled.table` + border-collapse: collapse; + + th, + td { + padding: 6px; + border: 1px solid var(--tertiary-foreground); + } + `, + ul: styled.ul` + list-style-position: inside; + padding-left: 10px; + margin: 0.2em 0; + `, + ol: styled.ol` + list-style-position: inside; + padding-left: 10px; + margin: 0.2em 0; + `, + li: styled.li` + ${(props) => + props.class === "task-list-item" && + css` + list-style-type: none; + `} + `, + blockquote: styled.blockquote` + margin: 2px 0; + padding: 2px 0; + background: var(--hover); + border-radius: var(--border-radius); + border-inline-start: 4px solid var(--tertiary-background); + + > * { + margin: 0 8px; + } + `, + // Block image elements + img: Null, + // Catch literally everything else just in case + video: Null, + figure: Null, + picture: Null, + source: Null, + audio: Null, + script: Null, + style: Null, +}; + +/** + * Unified Markdown renderer + */ +const render = unified() + .use(remarkParse) + .use(remarkBreaks) + .use(remarkGfm) + .use(remarkMath) + .use(remarkSpoiler) + .use(remarkChannels) + .use(remarkTimestamps) + .use(remarkEmoji) + .use(remarkMention) + .use(remarkRehype, { + handlers: { + ...passThroughComponents("emoji", "spoiler", "mention", "channel"), + timestamp: timestampHandler, + }, + }) + .use(rehypeKatex, { + maxSize: 10, + maxExpand: 0, + trust: false, + strict: false, + output: "html", + throwOnError: false, + errorColor: "var(--error)", + }) + .use(rehypePrism) + // @ts-expect-error typings do not + // match between Preact and React + .use(rehypeReact, { + createElement, + Fragment, + components, + }); + +/** + * Markdown parent container + */ +const Container = styled.div<{ largeEmoji: boolean }>` + .math-display { + overflow-x: auto; + } + + --emoji-size: ${(props) => (props.largeEmoji ? "3em" : "1.25em")}; +`; + +/** + * Remark renderer component + */ +export default memo(({ content, disallowBigEmoji }: MarkdownProps) => { + const [Content, setContent] = useState(null!); + + useEffect(() => { + render.process(content!).then((file) => setContent(file.result)); + }, [content]); + + const largeEmoji = useMemo( + () => !disallowBigEmoji && isOnlyEmoji(content!), + [content, disallowBigEmoji], + ); + + return {Content}; +}); diff --git a/src/components/markdown/Renderer.tsx b/src/components/markdown/Renderer.tsx deleted file mode 100644 index 73eaedd3..00000000 --- a/src/components/markdown/Renderer.tsx +++ /dev/null @@ -1,290 +0,0 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -import MarkdownKatex from "@traptitech/markdown-it-katex"; -import MarkdownSpoilers from "@traptitech/markdown-it-spoiler"; -import "katex/dist/katex.min.css"; -import MarkdownIt from "markdown-it"; -// @ts-expect-error No typings. -import MarkdownEmoji from "markdown-it-emoji/dist/markdown-it-emoji-bare"; -import { RE_MENTIONS } from "revolt.js"; - -import styles from "./Markdown.module.scss"; -import { useCallback, useContext } from "preact/hooks"; - -import { internalEmit } from "../../lib/eventEmitter"; -import { determineLink } from "../../lib/links"; - -import { dayjs } from "../../context/Locale"; - -import { emojiDictionary } from "../../assets/emojis"; -import { useClient } from "../../controllers/client/ClientController"; -import { modalController } from "../../controllers/modals/ModalController"; -import { generateEmoji } from "../common/Emoji"; -import { MarkdownProps } from "./Markdown"; -import Prism from "./prism"; - -// TODO: global.d.ts file for defining globals -declare global { - interface Window { - copycode: (element: HTMLDivElement) => void; - } -} - -// Handler for code block copy. -if (typeof window !== "undefined") { - window.copycode = function (element: HTMLDivElement) { - try { - const code = element.parentElement?.parentElement?.children[1]; - if (code) { - navigator.clipboard.writeText(code.textContent?.trim() ?? ""); - } - } catch (e) {} - }; -} - -export const md: MarkdownIt = MarkdownIt({ - breaks: true, - linkify: true, - highlight: (str, lang) => { - const v = Prism.languages[lang]; - if (v) { - const out = Prism.highlight(str, v, lang); - return `
${lang}
${out}
`; - } - - return `
${md.utils.escapeHtml(
-            str,
-        )}
`; - }, -}) - .disable("image") - .use(MarkdownEmoji, { defs: emojiDictionary }) - .use(MarkdownSpoilers) - .use(MarkdownKatex, { - throwOnError: false, - maxExpand: 0, - maxSize: 10, - strict: false, - errorColor: "var(--error)", - }); - -md.linkify.set({ fuzzyLink: false }); - -// TODO: global.d.ts file for defining globals -declare global { - interface Window { - internalHandleURL: (element: HTMLAnchorElement) => void; - } -} - -// Include emojis. -md.renderer.rules.emoji = function (token, idx) { - return generateEmoji(token[idx].content); -}; - -// Force line breaks. -// https://github.com/markdown-it/markdown-it/issues/211#issuecomment-508380611 -const defaultParagraphRenderer = - md.renderer.rules.paragraph_open || - ((tokens, idx, options, env, self) => - self.renderToken(tokens, idx, options)); - -md.renderer.rules.paragraph_open = function (tokens, idx, options, env, self) { - let result = ""; - if (idx > 1) { - const inline = tokens[idx - 2]; - const paragraph = tokens[idx]; - if ( - inline.type === "inline" && - inline.map && - inline.map[1] && - paragraph.map && - paragraph.map[0] - ) { - const diff = paragraph.map[0] - inline.map[1]; - if (diff > 0) { - result = "
".repeat(diff); - } - } - } - - return result + defaultParagraphRenderer(tokens, idx, options, env, self); -}; - -const RE_TWEMOJI = /:(\w+):/g; - -// ! FIXME: Move to library -const RE_CHANNELS = /<#([A-z0-9]{26})>/g; - -const RE_TIME = //g; - -export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) { - const client = useClient(); - - if (typeof content === "undefined") return null; - if (!content || content.length === 0) return null; - - // We replace the message with the mention at the time of render. - // We don't care if the mention changes. - const newContent = content - .replace(RE_TIME, (sub: string, ...args: unknown[]) => { - if (isNaN(args[0] as number)) return sub; - const date = dayjs.unix(args[0] as number); - const format = args[1] as string; - let final = ""; - switch (format) { - case "t": - final = date.format("hh:mm"); - break; - case "T": - final = date.format("hh:mm:ss"); - break; - case "R": - final = date.fromNow(); - break; - case "D": - final = date.format("DD MMMM YYYY"); - break; - case "F": - final = date.format("dddd, DD MMMM YYYY hh:mm"); - break; - default: - final = date.format("DD MMMM YYYY hh:mm"); - break; - } - return `\`${final}\``; - }) - .replace(RE_MENTIONS, (sub: string, ...args: unknown[]) => { - const id = args[0] as string, - user = client.users.get(id); - - if (user) { - return `[@${user.username}](/@${id})`; - } - - return sub; - }) - .replace(RE_CHANNELS, (sub: string, ...args: unknown[]) => { - const id = args[0] as string, - channel = client.channels.get(id); - - if ( - channel?.channel_type === "TextChannel" || - channel?.channel_type === "VoiceChannel" - ) { - return `[#${channel.name}](/server/${channel.server_id}/channel/${id})`; - } - - return sub; - }); - - const useLargeEmojis = disallowBigEmoji - ? false - : content.replace(RE_TWEMOJI, "").trim().length === 0; - - const toggle = useCallback((ev: MouseEvent) => { - if (ev.currentTarget) { - const element = ev.currentTarget as HTMLDivElement; - if (element.classList.contains("spoiler")) { - element.classList.add("shown"); - } - } - }, []); - - const handleLink = useCallback((ev: MouseEvent) => { - if (ev.currentTarget) { - const element = ev.currentTarget as HTMLAnchorElement; - - if (ev.shiftKey) { - switch (element.dataset.type) { - case "mention": { - internalEmit( - "MessageBox", - "append", - `<@${element.dataset.mentionId}>`, - "mention", - ); - ev.preventDefault(); - return; - } - case "channel_mention": { - internalEmit( - "MessageBox", - "append", - `<#${element.dataset.mentionId}>`, - "channel_mention", - ); - ev.preventDefault(); - return; - } - } - } - - if (modalController.openLink(element.href)) { - ev.preventDefault(); - } - } - }, []); - - return ( - { - if (el) { - el.querySelectorAll(".spoiler").forEach( - (element) => { - element.removeEventListener("click", toggle); - element.addEventListener("click", toggle); - }, - ); - - el.querySelectorAll("a").forEach( - (element) => { - element.removeEventListener("click", handleLink); - element.addEventListener("click", handleLink); - element.removeAttribute("data-type"); - element.removeAttribute("data-mention-id"); - element.removeAttribute("target"); - - const link = determineLink(element.href); - switch (link.type) { - case "profile": { - element.setAttribute( - "data-type", - "mention", - ); - element.setAttribute( - "data-mention-id", - link.id, - ); - break; - } - case "navigate": { - if (link.navigation_type === "channel") { - element.setAttribute( - "data-type", - "channel_mention", - ); - element.setAttribute( - "data-mention-id", - link.channel_id, - ); - } - break; - } - case "external": { - element.setAttribute("target", "_blank"); - element.setAttribute("rel", "noreferrer"); - break; - } - } - }, - ); - } - }} - className={styles.markdown} - dangerouslySetInnerHTML={{ - __html: md.render(newContent), - }} - data-large-emojis={useLargeEmojis} - /> - ); -} diff --git a/src/components/markdown/plugins/Codeblock.tsx b/src/components/markdown/plugins/Codeblock.tsx new file mode 100644 index 00000000..85f27ece --- /dev/null +++ b/src/components/markdown/plugins/Codeblock.tsx @@ -0,0 +1,78 @@ +import styled from "styled-components"; + +import { useCallback, useRef } from "preact/hooks"; + +import { Tooltip } from "@revoltchat/ui"; + +import { modalController } from "../../../controllers/modals/ModalController"; + +/** + * Base codeblock styles + */ +const Base = styled.pre` + padding: 1em; + overflow-x: scroll; + background: var(--block); + border-radius: var(--border-radius); +`; + +/** + * Copy codeblock contents button styles + */ +const Lang = styled.div` + width: fit-content; + padding-bottom: 8px; + + a { + color: #111; + cursor: pointer; + padding: 2px 6px; + font-weight: 600; + user-select: none; + display: inline-block; + background: var(--accent); + + font-size: 10px; + text-transform: uppercase; + box-shadow: 0 2px #787676; + border-radius: calc(var(--border-radius) / 3); + + &:active { + transform: translateY(1px); + box-shadow: 0 1px #787676; + } + } +`; + +/** + * Render a codeblock with copy text button + */ +export const RenderCodeblock: React.FC<{ class: string }> = ({ + children, + ...props +}) => { + const ref = useRef(null); + + let text = "text"; + if (props.class) { + text = props.class.split("-")[1]; + } + + const onCopy = useCallback(() => { + const text = ref.current?.querySelector("code")?.innerText; + text && modalController.writeText(text); + }, [ref]); + + return ( + + + + {/** + // @ts-expect-error Preact-React */} + {text} + + + {children} + + ); +}; diff --git a/src/components/markdown/plugins/anchors.tsx b/src/components/markdown/plugins/anchors.tsx new file mode 100644 index 00000000..6d0f52f0 --- /dev/null +++ b/src/components/markdown/plugins/anchors.tsx @@ -0,0 +1,34 @@ +import { Link } from "react-router-dom"; + +import { determineLink } from "../../../lib/links"; + +import { modalController } from "../../../controllers/modals/ModalController"; + +export function RenderAnchor({ + href, + ...props +}: JSX.HTMLAttributes) { + // Pass-through no href or if anchor + if (!href || href.startsWith("#")) return ; + + // Determine type of link + const link = determineLink(href); + if (link.type === "none") return ; + + // Render direct link if internal + if (link.type === "navigate") { + return ; + } + + return ( + + modalController.openLink(href) && ev.preventDefault() + } + /> + ); +} diff --git a/src/components/markdown/plugins/channels.tsx b/src/components/markdown/plugins/channels.tsx new file mode 100644 index 00000000..a4654fdc --- /dev/null +++ b/src/components/markdown/plugins/channels.tsx @@ -0,0 +1,21 @@ +import { Link } from "react-router-dom"; + +import { clientController } from "../../../controllers/client/ClientController"; +import { createComponent, CustomComponentProps } from "./remarkRegexComponent"; + +export function RenderChannel({ match }: CustomComponentProps) { + const channel = clientController.getAvailableClient().channels.get(match)!; + + return ( + {`#${channel.name}`} + ); +} + +export const remarkChannels = createComponent( + "channel", + /<#([A-z0-9]{26})>/g, + (match) => clientController.getAvailableClient().channels.has(match), +); diff --git a/src/components/markdown/plugins/emoji.tsx b/src/components/markdown/plugins/emoji.tsx new file mode 100644 index 00000000..f97e85bc --- /dev/null +++ b/src/components/markdown/plugins/emoji.tsx @@ -0,0 +1,43 @@ +import styled from "styled-components"; + +import { emojiDictionary } from "../../../assets/emojis"; +import { clientController } from "../../../controllers/client/ClientController"; +import { parseEmoji } from "../../common/Emoji"; +import { createComponent, CustomComponentProps } from "./remarkRegexComponent"; + +const Emoji = styled.img` + object-fit: contain; + + height: var(--emoji-size); + width: var(--emoji-size); + margin: 0 0.05em 0 0.1em; + vertical-align: -0.2em; +`; + +export function RenderEmoji({ match }: CustomComponentProps) { + return ( + + ); +} + +const RE_EMOJI = /:([a-zA-Z0-9_]+):/g; + +export const remarkEmoji = createComponent( + "emoji", + RE_EMOJI, + (match) => + match in emojiDictionary || + clientController.getAvailableClient().emojis?.has(match), +); + +export function isOnlyEmoji(text: string) { + return text.replaceAll(RE_EMOJI, "").trim().length === 0; +} diff --git a/src/components/markdown/plugins/mentions.tsx b/src/components/markdown/plugins/mentions.tsx new file mode 100644 index 00000000..1411dcf5 --- /dev/null +++ b/src/components/markdown/plugins/mentions.tsx @@ -0,0 +1,42 @@ +import { RE_MENTIONS } from "revolt.js"; +import styled from "styled-components"; + +import { clientController } from "../../../controllers/client/ClientController"; +import UserShort from "../../common/user/UserShort"; +import { createComponent, CustomComponentProps } from "./remarkRegexComponent"; + +const Mention = styled.a` + gap: 4px; + padding: 0 6px; + flex-shrink: 0; + align-items: center; + display: inline-flex; + + font-weight: 600; + background: var(--secondary-background); + border-radius: calc(var(--border-radius) * 2); + + &:hover { + text-decoration: none; + } + + svg { + width: 1em; + height: 1em; + } +`; + +export function RenderMention({ match }: CustomComponentProps) { + return ( + + + + ); +} + +export const remarkMention = createComponent("mention", RE_MENTIONS, (match) => + clientController.getAvailableClient().users.has(match), +); diff --git a/src/components/markdown/plugins/remarkRegexComponent.ts b/src/components/markdown/plugins/remarkRegexComponent.ts new file mode 100644 index 00000000..6693f7b5 --- /dev/null +++ b/src/components/markdown/plugins/remarkRegexComponent.ts @@ -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 = {}; + for (const key of keys) { + obj[key] = passThroughRehype(key); + } + return obj; +}; diff --git a/src/components/markdown/plugins/spoiler.tsx b/src/components/markdown/plugins/spoiler.tsx new file mode 100644 index 00000000..1986bde3 --- /dev/null +++ b/src/components/markdown/plugins/spoiler.tsx @@ -0,0 +1,45 @@ +import styled, { css } from "styled-components"; + +import { useState } from "preact/hooks"; + +import { createComponent, CustomComponentProps } from "./remarkRegexComponent"; + +const Spoiler = styled.span<{ shown: boolean }>` + padding: 0 2px; + cursor: pointer; + user-select: none; + color: transparent; + background: #151515; + border-radius: var(--border-radius); + + > * { + opacity: 0; + pointer-events: none; + } + + ${(props) => + props.shown && + css` + cursor: auto; + user-select: all; + color: var(--foreground); + background: var(--secondary-background); + + > * { + opacity: 1; + pointer-events: unset; + } + `} +`; + +export function RenderSpoiler({ match }: CustomComponentProps) { + const [shown, setShown] = useState(false); + + return ( + setShown(true)}> + {match} + + ); +} + +export const remarkSpoiler = createComponent("spoiler", /!!([^!]+)!!/g); diff --git a/src/components/markdown/plugins/timestamps.ts b/src/components/markdown/plugins/timestamps.ts new file mode 100644 index 00000000..a6760b24 --- /dev/null +++ b/src/components/markdown/plugins/timestamps.ts @@ -0,0 +1,39 @@ +import type { Handler } from "mdast-util-to-hast"; + +import { dayjs } from "../../../context/Locale"; + +import { createComponent } from "./remarkRegexComponent"; + +export const timestampHandler: Handler = (h, { match, arg1 }) => { + if (isNaN(match)) return { type: "text", value: match }; + const date = dayjs.unix(match); + + let value = ""; + switch (arg1) { + case "t": + value = date.format("hh:mm"); + break; + case "T": + value = date.format("hh:mm:ss"); + break; + case "R": + value = date.fromNow(); + break; + case "D": + value = date.format("DD MMMM YYYY"); + break; + case "F": + value = date.format("dddd, DD MMMM YYYY hh:mm"); + break; + default: + value = date.format("DD MMMM YYYY hh:mm"); + break; + } + + return h(null, "code", {}, [{ type: "text", value }]); +}; + +export const remarkTimestamps = createComponent( + "timestamp", + //g, +); diff --git a/src/controllers/modals/ModalController.tsx b/src/controllers/modals/ModalController.tsx index 0e4ac29a..c70f0487 100644 --- a/src/controllers/modals/ModalController.tsx +++ b/src/controllers/modals/ModalController.tsx @@ -210,10 +210,6 @@ class ModalControllerExtended extends ModalController { const settings = getApplicationState().settings; switch (link.type) { - case "profile": { - this.push({ type: "user_profile", user_id: link.id }); - break; - } case "navigate": { history.push(link.path); break; diff --git a/src/lib/links.ts b/src/lib/links.ts index 15a696e4..2a3801fe 100644 --- a/src/lib/links.ts +++ b/src/lib/links.ts @@ -1,11 +1,7 @@ type LinkType = - | { type: "profile"; id: string } - | { type: "navigate"; path: string; navigation_type?: null } | { type: "navigate"; path: string; - navigation_type: "channel"; - channel_id: string; } | { type: "external"; href: string; url: URL } | { type: "none" }; @@ -17,9 +13,6 @@ const ALLOWED_ORIGINS = [ "local.revolt.chat", ]; -const CHANNEL_PATH_RE = - /^\/server\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}\/channel\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/; - export function determineLink(href?: string): LinkType { let internal, url: URL | null = null; @@ -30,29 +23,12 @@ export function determineLink(href?: string): LinkType { if (ALLOWED_ORIGINS.includes(url.hostname)) { const path = url.pathname; - if (path.startsWith("/@")) { - const id = path.substr(2); - if (/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}/.test(id)) { - return { type: "profile", id }; - } - } else { - if (CHANNEL_PATH_RE.test(path)) { - return { - type: "navigate", - path, - navigation_type: "channel", - channel_id: path.slice(43), - }; - } - return { type: "navigate", path }; - } - - internal = true; + return { type: "navigate", path }; } } catch (err) {} if (!internal && url) { - if (url.protocol !== "javascript") { + if (!url.protocol.startsWith("javascript")) { return { type: "external", href, url }; } } diff --git a/yarn.lock b/yarn.lock index cd804219..428bc875 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2109,6 +2109,15 @@ __metadata: languageName: node linkType: hard +"@mapbox/hast-util-table-cell-style@npm:^0.2.0": + version: 0.2.0 + resolution: "@mapbox/hast-util-table-cell-style@npm:0.2.0" + dependencies: + unist-util-visit: ^1.4.1 + checksum: 4b05edda2be32e3286860bd5b50eddc8fe7d64c88de49511c9188e0e1d7c2fcba3f589a279d87cf8eb42ed5ef9ef0e788d2fcb103613e547cb3143b1bd29c49a + languageName: node + linkType: hard + "@mdn/browser-compat-data@npm:^3.3.14": version: 3.3.14 resolution: "@mdn/browser-compat-data@npm:3.3.14" @@ -2231,9 +2240,9 @@ __metadata: languageName: node linkType: hard -"@revoltchat/ui@npm:1.0.69": - version: 1.0.69 - resolution: "@revoltchat/ui@npm:1.0.69" +"@revoltchat/ui@npm:1.0.70": + version: 1.0.70 + resolution: "@revoltchat/ui@npm:1.0.70" dependencies: "@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0 @@ -2247,7 +2256,7 @@ __metadata: react-virtuoso: ^2.12.0 peerDependencies: revolt.js: "*" - checksum: bb67870911689d7dccd82f96affdb0e3e89b2e688d4a7403ee9bbb6a0245dfc296f35636ababd5ed6b43a3ce26777cee03c453728088b4a29fed350dcc89ea37 + checksum: 70eba00f8b2d4fed3f83cdd64488ab4eb99acc9dd0427333a41f203912d9664f878ec04300bc3eaea2a67fef1620472cce9ff7048ef1280f950825593cf316a7 languageName: node linkType: hard @@ -2490,7 +2499,7 @@ __metadata: languageName: node linkType: hard -"@types/debug@npm:^4.1.6": +"@types/debug@npm:^4.0.0, @types/debug@npm:^4.1.6": version: 4.1.7 resolution: "@types/debug@npm:4.1.7" dependencies: @@ -2513,6 +2522,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:*, @types/hast@npm:^2.0.0": + version: 2.3.4 + resolution: "@types/hast@npm:2.3.4" + dependencies: + "@types/unist": "*" + checksum: fff47998f4c11e21a7454b58673f70478740ecdafd95aaf50b70a3daa7da9cdc57315545bf9c039613732c40b7b0e9e49d11d03fe9a4304721cdc3b29a88141e + languageName: node + linkType: hard + "@types/history@npm:*": version: 4.7.9 resolution: "@types/history@npm:4.7.9" @@ -2544,10 +2562,10 @@ __metadata: languageName: node linkType: hard -"@types/linkify-it@npm:*": - version: 3.0.2 - resolution: "@types/linkify-it@npm:3.0.2" - checksum: dff8f10fafb885422474e456596f12d518ec4cdd6c33cca7a08e7c86b912d301ed91cf5a7613e148c45a12600dc9ab3d85ad16d5b48dc1aaeda151a68f16b536 +"@types/katex@npm:^0.11.0": + version: 0.11.1 + resolution: "@types/katex@npm:0.11.1" + checksum: 1e51988b4b386a1b6fa8e22826ab4705bf3e6c9fb03461f2c91d28cb31095232bdeff491069ac9bc74bc4c26110be6a11a41e12ca77a2e4169f3afd8cd349355 languageName: node linkType: hard @@ -2590,18 +2608,16 @@ __metadata: languageName: node linkType: hard -"@types/markdown-it@npm:^12.0.2": - version: 12.2.1 - resolution: "@types/markdown-it@npm:12.2.1" +"@types/mdast@npm:*, @types/mdast@npm:^3.0.0": + version: 3.0.10 + resolution: "@types/mdast@npm:3.0.10" dependencies: - "@types/linkify-it": "*" - "@types/mdurl": "*" - highlight.js: ^10.7.2 - checksum: e3f367b7006e4ade2b8faa7125f06f6e5dd24ff762faedad7c55d1c99dc9905213a1021c7ae019ed1693814ec31fef25be028415d4d3061186bbf2d67646ddcb + "@types/unist": "*" + checksum: 3f587bfc0a9a2403ecadc220e61031b01734fedaf82e27eb4d5ba039c0eb54db8c85681ccc070ab4df3f7ec711b736a82b990e69caa14c74bf7ac0ccf2ac7313 languageName: node linkType: hard -"@types/mdurl@npm:*": +"@types/mdurl@npm:^1.0.0": version: 1.0.2 resolution: "@types/mdurl@npm:1.0.2" checksum: 79c7e523b377f53cf1f5a240fe23d0c6cae856667692bd21bf1d064eafe5ccc40ae39a2aa0a9a51e8c94d1307228c8f6b121e847124591a9a828c3baf65e86e2 @@ -2622,6 +2638,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>12": + version: 18.0.3 + resolution: "@types/node@npm:18.0.3" + checksum: 5dec59fbbc1186c808b53df1ca717dad034dbd6a901c75f5b052c845618b531b05f27217122c6254db99529a68618e4cfc534ae3dbf4e88754e9e572df80defa + languageName: node + linkType: hard + "@types/node@npm:^15.12.4": version: 15.14.9 resolution: "@types/node@npm:15.14.9" @@ -2636,6 +2659,13 @@ __metadata: languageName: node linkType: hard +"@types/parse5@npm:^6.0.0": + version: 6.0.3 + resolution: "@types/parse5@npm:6.0.3" + checksum: ddb59ee4144af5dfcc508a8dcf32f37879d11e12559561e65788756b95b33e6f03ea027d88e1f5408f9b7bfb656bf630ace31a2169edf44151daaf8dd58df1b7 + languageName: node + linkType: hard + "@types/preact-i18n@npm:^2.3.0": version: 2.3.1 resolution: "@types/preact-i18n@npm:2.3.1" @@ -2652,6 +2682,13 @@ __metadata: languageName: node linkType: hard +"@types/prismjs@npm:^1.16.6": + version: 1.26.0 + resolution: "@types/prismjs@npm:1.26.0" + checksum: cd5e7a6214c1f4213ec512a5fcf6d8fe37a56b813fc57ac95b5ff5ee074742bfdbd2f2730d9fd985205bf4586728e09baa97023f739e5aa1c9735a7c1ecbd11a + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.4 resolution: "@types/prop-types@npm:15.7.4" @@ -2778,6 +2815,13 @@ __metadata: languageName: node linkType: hard +"@types/unist@npm:*, @types/unist@npm:^2.0.0": + version: 2.0.6 + resolution: "@types/unist@npm:2.0.6" + checksum: 25cb860ff10dde48b54622d58b23e66214211a61c84c0f15f88d38b61aa1b53d4d46e42b557924a93178c501c166aa37e28d7f6d994aba13d24685326272d5db + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^4.27.0": version: 4.29.3 resolution: "@typescript-eslint/eslint-plugin@npm:4.29.3" @@ -3301,6 +3345,13 @@ __metadata: languageName: node linkType: hard +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: aab4e8ccdc8d762bf3fdfce8e706601695620c0c2eda256dd85088dc0be3cfd7ff126f6e99c2bee1f24f5d418414aacf09d7f9702f16d6963df2fa488cda8824 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -3315,6 +3366,13 @@ __metadata: languageName: node linkType: hard +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 3e25c80ef626c3a3487c73dbfc70ac322ec830666c9ad915d11b701142fab25ec1e63eff2c450c74347acfd2de854ccde865cd79ef4db1683f7c7b046ea43bb0 + languageName: node + linkType: hard + "bowser@npm:^2.11.0": version: 2.11.0 resolution: "bowser@npm:2.11.0" @@ -3458,6 +3516,13 @@ __metadata: languageName: node linkType: hard +"ccount@npm:^2.0.0": + version: 2.0.1 + resolution: "ccount@npm:2.0.1" + checksum: 48193dada54c9e260e0acf57fc16171a225305548f9ad20d5471e0f7a8c026aedd8747091dccb0d900cde7df4e4ddbd235df0d8de4a64c71b12f0d3303eeafd4 + languageName: node + linkType: hard + "chalk@npm:^2.0.0, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -3479,6 +3544,13 @@ __metadata: languageName: node linkType: hard +"character-entities@npm:^2.0.0": + version: 2.0.2 + resolution: "character-entities@npm:2.0.2" + checksum: cf1643814023697f725e47328fcec17923b8f1799102a8a79c1514e894815651794a2bffd84bb1b3a4b124b050154e4529ed6e81f7c8068a734aecf07a6d3def + languageName: node + linkType: hard + "charcodes@npm:^0.2.0": version: 0.2.0 resolution: "charcodes@npm:0.2.0" @@ -3554,7 +3626,7 @@ __metadata: "@hcaptcha/react-hcaptcha": ^0.3.6 "@insertish/vite-plugin-babel-macros": ^1.0.5 "@preact/preset-vite": ^2.0.0 - "@revoltchat/ui": 1.0.69 + "@revoltchat/ui": 1.0.70 "@rollup/plugin-replace": ^2.4.2 "@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0 @@ -3567,7 +3639,6 @@ __metadata: "@types/lodash": ^4 "@types/lodash.defaultsdeep": ^4.6.6 "@types/lodash.isequal": ^4.5.5 - "@types/markdown-it": ^12.0.2 "@types/node": ^15.12.4 "@types/preact-i18n": ^2.3.0 "@types/prismjs": ^1.16.5 @@ -3599,8 +3670,7 @@ __metadata: lodash.defaultsdeep: ^4.6.1 lodash.isequal: ^4.5.0 long: ^5.2.0 - markdown-it: ^12.0.6 - markdown-it-emoji: ^2.0.0 + mdast-util-to-hast: ^12.1.2 mediasoup-client: "npm:@insertish/mediasoup-client@3.6.36-esnext" mobx: ^6.6.0 mobx-react-lite: 3.4.0 @@ -3618,7 +3688,15 @@ __metadata: react-router-dom: ^5.2.0 react-scroll: ^1.8.2 react-virtuoso: ^2.12.0 - revolt.js: 6.0.3 + rehype-katex: ^6.0.2 + rehype-prism: ^2.1.3 + rehype-react: ^7.1.1 + remark-breaks: ^3.0.2 + remark-gfm: ^3.0.1 + remark-math: ^5.1.1 + remark-parse: ^10.0.1 + remark-rehype: ^10.1.0 + revolt.js: 6.0.5 rimraf: ^3.0.2 sass: ^1.35.1 semver: ^7.3.7 @@ -3631,6 +3709,8 @@ __metadata: styled-components: ^5.3.0 typescript: ^4.4.2 ulid: ^2.3.0 + unified: ^10.1.2 + unist-util-visit: ^4.1.0 use-resize-observer: ^7.0.0 vite: ^2.6.14 vite-plugin-pwa: ^0.11.13 @@ -3712,6 +3792,13 @@ __metadata: languageName: node linkType: hard +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "comma-separated-tokens@npm:2.0.2" + checksum: 8fa68ff2605233571536a802a7c712b0c766e0c5088e067be72740054e84d040865eea945c984924ae84932bcc3e25a99f71601220b438e875b5f42b87277767 + languageName: node + linkType: hard + "commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -3726,6 +3813,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^8.0.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 + languageName: node + linkType: hard + "comment-parser@npm:1.3.1": version: 1.3.1 resolution: "comment-parser@npm:1.3.1" @@ -3848,6 +3942,13 @@ __metadata: languageName: node linkType: hard +"css-selector-parser@npm:^1.0.0": + version: 1.4.1 + resolution: "css-selector-parser@npm:1.4.1" + checksum: 31948754e579eedb918c2fb2d5a4c643ec769ff4a0d03a7bd10b43b25d44973f8cbe86d7ec00c4494269f7ff38b3d2ab0f6ea801cece0ef0974e74469dff770c + languageName: node + linkType: hard + "css-to-react-native@npm:^3.0.0": version: 3.0.0 resolution: "css-to-react-native@npm:3.0.0" @@ -3873,7 +3974,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -3909,6 +4010,15 @@ __metadata: languageName: node linkType: hard +"decode-named-character-reference@npm:^1.0.0": + version: 1.0.2 + resolution: "decode-named-character-reference@npm:1.0.2" + dependencies: + character-entities: ^2.0.0 + checksum: f4c71d3b93105f20076052f9cb1523a22a9c796b8296cd35eef1ca54239c78d182c136a848b83ff8da2071e3ae2b1d300bf29d00650a6d6e675438cc31b11d78 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.3 resolution: "deep-is@npm:0.1.3" @@ -3946,6 +4056,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.0": + version: 2.0.2 + resolution: "dequal@npm:2.0.2" + checksum: 86c7a2c59f7b0797ed397c74b5fcdb744e48fc19440b70ad6ac59f57550a96b0faef3f1cfd5760ec5e6d3f7cb101f634f1f80db4e727b1dc8389bf62d977c0a0 + languageName: node + linkType: hard + "detect-browser@npm:^5.2.0": version: 5.2.0 resolution: "detect-browser@npm:5.2.0" @@ -3953,6 +4070,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^5.0.0": + version: 5.1.0 + resolution: "diff@npm:5.1.0" + checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -4046,13 +4170,6 @@ __metadata: languageName: node linkType: hard -"entities@npm:~2.1.0": - version: 2.1.0 - resolution: "entities@npm:2.1.0" - checksum: a10a877e489586a3f6a691fe49bf3fc4e58f06c8e80522f08214a5150ba457e7017b447d4913a3fa041bda06ee4c92517baa4d8d75373eaa79369e9639225ffd - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -4351,6 +4468,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e + languageName: node + linkType: hard + "eslint-config-preact@npm:^1.1.4": version: 1.1.4 resolution: "eslint-config-preact@npm:1.1.4" @@ -4642,6 +4766,13 @@ __metadata: languageName: node linkType: hard +"extend@npm:^3.0.0": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 + languageName: node + linkType: hard + "fake-mediastreamtrack@npm:^1.1.6": version: 1.1.6 resolution: "fake-mediastreamtrack@npm:1.1.6" @@ -5083,10 +5214,84 @@ __metadata: languageName: node linkType: hard -"highlight.js@npm:^10.7.2": - version: 10.7.3 - resolution: "highlight.js@npm:10.7.3" - checksum: defeafcd546b535d710d8efb8e650af9e3b369ef53e28c3dc7893eacfe263200bba4c5fcf43524ae66d5c0c296b1af0870523ceae3e3104d24b7abf6374a4fea +"hast-to-hyperscript@npm:^10.0.0": + version: 10.0.1 + resolution: "hast-to-hyperscript@npm:10.0.1" + dependencies: + "@types/unist": ^2.0.0 + comma-separated-tokens: ^2.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + style-to-object: ^0.3.0 + unist-util-is: ^5.0.0 + web-namespaces: ^2.0.0 + checksum: 0ec7a6f873312421c6cfa84f8c842fa00c74e96018c371ace4800fda6590e208db8e31d4e84b09e436fe6b9b87b2fd2968b30c27881ff82fc9fe466a0f59b922 + languageName: node + linkType: hard + +"hast-util-from-parse5@npm:^7.0.0": + version: 7.1.0 + resolution: "hast-util-from-parse5@npm:7.1.0" + dependencies: + "@types/hast": ^2.0.0 + "@types/parse5": ^6.0.0 + "@types/unist": ^2.0.0 + hastscript: ^7.0.0 + property-information: ^6.0.0 + vfile: ^5.0.0 + vfile-location: ^4.0.0 + web-namespaces: ^2.0.0 + checksum: 4a774700042e03aeecca6b6977f0e915069eefcf81c30d59ae0e1d2d7170e419065bcd8708504cb7b4d19b05367daee2177ddce47db1b5a654bb7ec19ba8d227 + languageName: node + linkType: hard + +"hast-util-is-element@npm:^2.0.0": + version: 2.1.2 + resolution: "hast-util-is-element@npm:2.1.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/unist": ^2.0.0 + checksum: c5fe9f7cde3775d4cbe19a9a55631a80b7a4ea0131fc2e3d097ebe228a35f09b9219f64b788b7a9cf819e6dcb6d1fc7830fd2f10ad536649e436e8c83da41e00 + languageName: node + linkType: hard + +"hast-util-parse-selector@npm:^3.0.0": + version: 3.1.0 + resolution: "hast-util-parse-selector@npm:3.1.0" + dependencies: + "@types/hast": ^2.0.0 + checksum: 8be1a2334652866b40fde72a8b7d0867a791ce8a70d15fd7bb44b9a4f349913b77999e5add41900466bc9461c6b0fdea391875ef534b33cacf7a2aee9d8e447c + languageName: node + linkType: hard + +"hast-util-to-text@npm:^3.1.0": + version: 3.1.1 + resolution: "hast-util-to-text@npm:3.1.1" + dependencies: + "@types/hast": ^2.0.0 + hast-util-is-element: ^2.0.0 + unist-util-find-after: ^4.0.0 + checksum: 2312a818c8ec7b02307b04175357e5a7a9918f48624d05366668ba60918734ca62b0ee21006a2a448e0e5a198654cd1fa4ba8c813702b465cb487e2320db523a + languageName: node + linkType: hard + +"hast-util-whitespace@npm:^2.0.0": + version: 2.0.0 + resolution: "hast-util-whitespace@npm:2.0.0" + checksum: abeb5386075bfb0facfce89eed0e13d2cb27a0910cec8fd234b48821a1538387a73fa7f458842e8c404148dc69434acbc10488d75b02817e460652c2c894c024 + languageName: node + linkType: hard + +"hastscript@npm:^7.0.0": + version: 7.0.2 + resolution: "hastscript@npm:7.0.2" + dependencies: + "@types/hast": ^2.0.0 + comma-separated-tokens: ^2.0.0 + hast-util-parse-selector: ^3.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + checksum: ee33aff714b12f9f83049550956c7fb3e5ac7bdd20e77b57dc01b66de06e8bb0b3ba24153d4b6a1d7fa660bfef91125ac29e1bb04fb628e30d11097d28037235 languageName: node linkType: hard @@ -5242,6 +5447,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:0.1.1": + version: 0.1.1 + resolution: "inline-style-parser@npm:0.1.1" + checksum: 5d545056a3e1f2bf864c928a886a0e1656a3517127d36917b973de581bd54adc91b4bf1febcb0da054f204b4934763f1a4e09308b4d55002327cf1d48ac5d966 + languageName: node + linkType: hard + "internal-slot@npm:^1.0.3": version: 1.0.3 resolution: "internal-slot@npm:1.0.3" @@ -5295,6 +5507,13 @@ __metadata: languageName: node linkType: hard +"is-buffer@npm:^2.0.0": + version: 2.0.5 + resolution: "is-buffer@npm:2.0.5" + checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42 + languageName: node + linkType: hard + "is-callable@npm:^1.1.4, is-callable@npm:^1.2.3, is-callable@npm:^1.2.4": version: 1.2.4 resolution: "is-callable@npm:1.2.4" @@ -5394,6 +5613,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.0.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce + languageName: node + linkType: hard + "is-plain-object@npm:^5.0.0": version: 5.0.0 resolution: "is-plain-object@npm:5.0.0" @@ -5652,6 +5878,17 @@ __metadata: languageName: node linkType: hard +"katex@npm:^0.13.0": + version: 0.13.24 + resolution: "katex@npm:0.13.24" + dependencies: + commander: ^8.0.0 + bin: + katex: cli.js + checksum: 1b7c8295867073d0db4f6fb41ef1c0e3418b8e23924ff61b446b36134cb74cdadc7242dfbfb922d9c32f0b15eda6160a08cd30948c4e78141966ca2991a1726b + languageName: node + linkType: hard + "katex@npm:^0.13.9": version: 0.13.16 resolution: "katex@npm:0.13.16" @@ -5663,6 +5900,17 @@ __metadata: languageName: node linkType: hard +"katex@npm:^0.15.0": + version: 0.15.6 + resolution: "katex@npm:0.15.6" + dependencies: + commander: ^8.0.0 + bin: + katex: cli.js + checksum: 2da808bbd1d3be27715006cd86767dd3fcce3e317fb3bbd64d407328d2d90de17b5d83062b2cfd0e0d0de32e340efbac214862bc96892a5d1492462e553728d4 + languageName: node + linkType: hard + "klaw@npm:^3.0.0": version: 3.0.0 resolution: "klaw@npm:3.0.0" @@ -5679,6 +5927,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^4.0.3": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 1dc476e32741acf0b1b5b0627ffd0d722e342c1b0da14de3e8ae97821327ca08f9fb944542fb3c126d90ac5f27f9d804edbe7c585bf7d12ef495d115e0f22c12 + languageName: node + linkType: hard + "kolorist@npm:^1.2.10": version: 1.5.0 resolution: "kolorist@npm:1.5.0" @@ -5719,15 +5974,6 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^3.0.1": - version: 3.0.2 - resolution: "linkify-it@npm:3.0.2" - dependencies: - uc.micro: ^1.0.1 - checksum: 08e14854ec3c29e3578311b1cd95e469952a27f191633189a23890628939fc45c6d84fa4495abb9f7f06e60f73a31b8881d834214864d46db914a09ffc7889ae - languageName: node - linkType: hard - "local-access@npm:^1.0.1": version: 1.1.0 resolution: "local-access@npm:1.1.0" @@ -5837,6 +6083,13 @@ __metadata: languageName: node linkType: hard +"longest-streak@npm:^3.0.0": + version: 3.0.1 + resolution: "longest-streak@npm:3.0.1" + checksum: 3b59c4c04ce3c70f137e339c10d574026fa3a711c45dc0e69a63a2c0ac981e57f837e1d5b64b991eee5234c4fa46fa10886a20626fb739ed3b04b77bcf6d14a8 + languageName: node + linkType: hard + "loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -5897,29 +6150,177 @@ __metadata: languageName: node linkType: hard -"markdown-it-emoji@npm:^2.0.0": - version: 2.0.0 - resolution: "markdown-it-emoji@npm:2.0.0" - checksum: 7d25844134d98a4e2cf70d9a14ea4ec5afc3f64740d69c88012795c8a24ed2f286850b72fd4437d60d2fe34f73cc1889a7e8e3ab4663f4fc3a4899991601bbba +"markdown-table@npm:^3.0.0": + version: 3.0.2 + resolution: "markdown-table@npm:3.0.2" + checksum: 7bd9eb54e7ac15165f79730ac6357b8194294552f727bcb34e29f3f1b72823c1220cb61153ebf0962c8faac4d25e49c62e8e9471cd6352a67cdca99928ecade1 languageName: node linkType: hard -"markdown-it@npm:^12.0.6": - version: 12.2.0 - resolution: "markdown-it@npm:12.2.0" +"mdast-util-definitions@npm:^5.0.0": + version: 5.1.1 + resolution: "mdast-util-definitions@npm:5.1.1" dependencies: - argparse: ^2.0.1 - entities: ~2.1.0 - linkify-it: ^3.0.1 - mdurl: ^1.0.1 - uc.micro: ^1.0.5 - bin: - markdown-it: bin/markdown-it.js - checksum: 8e3d6646edf8e7ef19ed707c59d16741bd40804f1e7567407efd2f346ae0f7ddcdeada83e7affebd21b9d7d947b27fc60fd795a970461785030a4e52e750122b + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + unist-util-visit: ^4.0.0 + checksum: f8025e2c35f6f8641528037abe18f492ef100e00a48c92cf78b7a313f9ccdb0e30c6aed0b40539767a3f425be09e78cb0f2f9bc4131fff41ea4664a1a7314a14 languageName: node linkType: hard -"mdurl@npm:^1.0.1": +"mdast-util-find-and-replace@npm:^2.0.0": + version: 2.2.0 + resolution: "mdast-util-find-and-replace@npm:2.2.0" + dependencies: + escape-string-regexp: ^5.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^5.0.0 + checksum: 1ca772fcecc07a1c61c115df1185b4454c830f7f5c7c5bcf34957af58af6b93c355c6d324afa8f6de33c4ad1a338e426ff391ffb7e4686a6deeae091a4c0eeaa + languageName: node + linkType: hard + +"mdast-util-from-markdown@npm:^1.0.0": + version: 1.2.0 + resolution: "mdast-util-from-markdown@npm:1.2.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + decode-named-character-reference: ^1.0.0 + mdast-util-to-string: ^3.1.0 + micromark: ^3.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-decode-string: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + unist-util-stringify-position: ^3.0.0 + uvu: ^0.5.0 + checksum: fadc3521a3d95f4adbadad462ca27c28b3bfe08740ae158dc0c4a22329bf5593254d98b8fd4024ecad8c47c77ec275454dfacfb907ff1b98ff8f5de25c716d40 + languageName: node + linkType: hard + +"mdast-util-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.2 + resolution: "mdast-util-gfm-autolink-literal@npm:1.0.2" + dependencies: + "@types/mdast": ^3.0.0 + ccount: ^2.0.0 + mdast-util-find-and-replace: ^2.0.0 + micromark-util-character: ^1.0.0 + checksum: 75e12f21ec24552ba33725f69a06cd703e5586d2296ca9d180927b2293c036e1bd39108adba83e8cbbefcc45ffd8821fb561b4c107684ed87bd9e5e286ba03bd + languageName: node + linkType: hard + +"mdast-util-gfm-footnote@npm:^1.0.0": + version: 1.0.1 + resolution: "mdast-util-gfm-footnote@npm:1.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + micromark-util-normalize-identifier: ^1.0.0 + checksum: 4caf69058b438c9e34004acfb1d2b20d58306898d760b889f73d27ed5702cd940be9fcb2a08f6e58b8d9d8e2b1c886c549cd7d23b659da5fb2ed87a22f44c13c + languageName: node + linkType: hard + +"mdast-util-gfm-strikethrough@npm:^1.0.0": + version: 1.0.1 + resolution: "mdast-util-gfm-strikethrough@npm:1.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: ce81222ab4c130516278f8db57be23bd529e9f8c30bb16ab5b2bf294c0dfd57f2dc7a010deede65f349a8d37be73f90dbaecd962f76f70befa8f43bcd32fe5b9 + languageName: node + linkType: hard + +"mdast-util-gfm-table@npm:^1.0.0": + version: 1.0.4 + resolution: "mdast-util-gfm-table@npm:1.0.4" + dependencies: + markdown-table: ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 56d9f0376b3da3e4cc0f5047d62a4eefa765934a1084822bc7804e7cf93c458c4bff2a917fa4e89c917287431a7284b656bf23ef89553e943d7f853ffefae693 + languageName: node + linkType: hard + +"mdast-util-gfm-task-list-item@npm:^1.0.0": + version: 1.0.1 + resolution: "mdast-util-gfm-task-list-item@npm:1.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 9bb0f162532f8e11e571802ed19301572479fe9507652c8fb3f648279bbde3baa9f6377d9492dbba61eedd96755f8aff9c7c259287875544fb751907d79da69e + languageName: node + linkType: hard + +"mdast-util-gfm@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-gfm@npm:2.0.1" + dependencies: + mdast-util-from-markdown: ^1.0.0 + mdast-util-gfm-autolink-literal: ^1.0.0 + mdast-util-gfm-footnote: ^1.0.0 + mdast-util-gfm-strikethrough: ^1.0.0 + mdast-util-gfm-table: ^1.0.0 + mdast-util-gfm-task-list-item: ^1.0.0 + mdast-util-to-markdown: ^1.0.0 + checksum: 8b39e6694521094ae28d12cbeff074ef3ec3f7f7ec59fbddd4e8a45a275e092c6ba6ecee4c720938eb3ee072ebd41d743b08cc0ab9171612a5aeddc1e78ae882 + languageName: node + linkType: hard + +"mdast-util-math@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-math@npm:2.0.1" + dependencies: + "@types/mdast": ^3.0.0 + longest-streak: ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 7576b466276717198fc461501d40c930be50b1754ca82979bd16df08e5fb7b959587c2090fbe044f050cf3f24c5c6febf3756a96031ecabe4b82f11d38c74546 + languageName: node + linkType: hard + +"mdast-util-to-hast@npm:^12.1.0, mdast-util-to-hast@npm:^12.1.2": + version: 12.1.2 + resolution: "mdast-util-to-hast@npm:12.1.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + "@types/mdurl": ^1.0.0 + mdast-util-definitions: ^5.0.0 + mdurl: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + trim-lines: ^3.0.0 + unist-builder: ^3.0.0 + unist-util-generated: ^2.0.0 + unist-util-position: ^4.0.0 + unist-util-visit: ^4.0.0 + checksum: 7bb888e73ce2ffc67d57868f3f7190fc8f30dda7b1293a14dd9b8fe6e72432f63db5c1e3015f9743c9f5bb17b33f622e3cea60bd54679395c9bc1949c16e4ce7 + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^1.0.0, mdast-util-to-markdown@npm:^1.3.0": + version: 1.3.0 + resolution: "mdast-util-to-markdown@npm:1.3.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + longest-streak: ^3.0.0 + mdast-util-to-string: ^3.0.0 + micromark-util-decode-string: ^1.0.0 + unist-util-visit: ^4.0.0 + zwitch: ^2.0.0 + checksum: 0ea4fc11b7a49b15d400d50044429c45222cb9bc583553288c7c54704d051f25049233817129ba56a6f581f1e20916e5c540870a80987318747a95b44a36ba3e + languageName: node + linkType: hard + +"mdast-util-to-string@npm:^3.0.0, mdast-util-to-string@npm:^3.1.0": + version: 3.1.0 + resolution: "mdast-util-to-string@npm:3.1.0" + checksum: f42ddd4e22f2215a75715b92ea6e3149c4ba356e7781d7b94fc86ded1c79cec3f986afeecef3a4a80068c9b224a6520099783a12146b957de24f020a3e47dd29 + languageName: node + linkType: hard + +"mdurl@npm:^1.0.0": version: 1.0.1 resolution: "mdurl@npm:1.0.1" checksum: 71731ecba943926bfbf9f9b51e28b5945f9411c4eda80894221b47cc105afa43ba2da820732b436f0798fd3edbbffcd1fc1415843c41a87fea08a41cc1e3d02b @@ -5965,6 +6366,352 @@ __metadata: languageName: node linkType: hard +"micromark-core-commonmark@npm:^1.0.0, micromark-core-commonmark@npm:^1.0.1": + version: 1.0.6 + resolution: "micromark-core-commonmark@npm:1.0.6" + dependencies: + decode-named-character-reference: ^1.0.0 + micromark-factory-destination: ^1.0.0 + micromark-factory-label: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-factory-title: ^1.0.0 + micromark-factory-whitespace: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-chunked: ^1.0.0 + micromark-util-classify-character: ^1.0.0 + micromark-util-html-tag-name: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-subtokenize: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.1 + uvu: ^0.5.0 + checksum: 4b483c46077f696ed310f6d709bb9547434c218ceb5c1220fde1707175f6f68b44da15ab8668f9c801e1a123210071e3af883a7d1215122c913fd626f122bfc2 + languageName: node + linkType: hard + +"micromark-extension-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-gfm-autolink-literal@npm:1.0.3" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: bb181972ac346ca73ca1ab0b80b80c9d6509ed149799d2217d5442670f499c38a94edff73d32fa52b390d89640974cfbd7f29e4ad7d599581d5e1cabcae636a2 + languageName: node + linkType: hard + +"micromark-extension-gfm-footnote@npm:^1.0.0": + version: 1.0.4 + resolution: "micromark-extension-gfm-footnote@npm:1.0.4" + dependencies: + micromark-core-commonmark: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 8daa203f5cf753338d5ecdbaae6b3ab6319d34b6013b90ea6860bed299418cecf86e69e48dabe42562e334760c738c77c5acdb47e75ae26f5f01f02f3bf0952d + languageName: node + linkType: hard + +"micromark-extension-gfm-strikethrough@npm:^1.0.0": + version: 1.0.4 + resolution: "micromark-extension-gfm-strikethrough@npm:1.0.4" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-classify-character: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: f43d316b85fe93df1711cdcdc99a5320b941239349234bd262fc708cb67ad47bdfb41d1a7ebe2a5829816b0e9d3107380a5c1e558cb536a75354cbe4857823ba + languageName: node + linkType: hard + +"micromark-extension-gfm-table@npm:^1.0.0": + version: 1.0.5 + resolution: "micromark-extension-gfm-table@npm:1.0.5" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: f0aab3b4333cc24b1534b08dc4cce986dd606df8b7ed913e5a1de9fe2d3ae67b2435663c0bc271b528874af4928e580e1ad540ea9117d7f2d74edb28859c97ef + languageName: node + linkType: hard + +"micromark-extension-gfm-tagfilter@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-extension-gfm-tagfilter@npm:1.0.1" + dependencies: + micromark-util-types: ^1.0.0 + checksum: 63e8d68f25871722900a67a8001d5da21f19ea707f3566fc7d0b2eb1f6d52476848bb6a41576cf22470565124af9497c5aae842355faa4c14ec19cb1847e71ec + languageName: node + linkType: hard + +"micromark-extension-gfm-task-list-item@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-gfm-task-list-item@npm:1.0.3" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: d320b0c5301f87e211c06a2330d1ee0fee6da14f0d6d44d5211055b465dadff34390cd6b258a5e0ca376fcda3364fef9a12fe6e26a0c858231fa3b98ddbf7785 + languageName: node + linkType: hard + +"micromark-extension-gfm@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-extension-gfm@npm:2.0.1" + dependencies: + micromark-extension-gfm-autolink-literal: ^1.0.0 + micromark-extension-gfm-footnote: ^1.0.0 + micromark-extension-gfm-strikethrough: ^1.0.0 + micromark-extension-gfm-table: ^1.0.0 + micromark-extension-gfm-tagfilter: ^1.0.0 + micromark-extension-gfm-task-list-item: ^1.0.0 + micromark-util-combine-extensions: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: b181479c87be38d5ae8d28e6dc52fab73c894fd2706876746f27a91fb186644ce03532a9c35dca2186327a0e2285cd5242ad0361dc89adedd4a50376ffd94e22 + languageName: node + linkType: hard + +"micromark-extension-math@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-extension-math@npm:2.0.2" + dependencies: + "@types/katex": ^0.11.0 + katex: ^0.13.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: c604d2e75443cd20988c485ecf35ff6799497b038d24e5c680107ebb6756031225df9292e449914178f04b837379b5f95dea0ad3fe4ae77ce60d194f102576a5 + languageName: node + linkType: hard + +"micromark-factory-destination@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-destination@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 8e733ae9c1c2342f14ff290bf09946e20f6f540117d80342377a765cac48df2ea5e748f33c8b07501ad7a43414b1a6597c8510ede2052b6bf1251fab89748e20 + languageName: node + linkType: hard + +"micromark-factory-label@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-factory-label@npm:1.0.2" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 957e9366bdc8dbc1437c0706ff96972fa985ab4b1274abcae12f6094f527cbf5c69e7f2304c23c7f4b96e311ff7911d226563b8b43dcfcd4091e8c985fb97ce6 + languageName: node + linkType: hard + +"micromark-factory-space@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-space@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 70d3aafde4e68ef4e509a3b644e9a29e4aada00801279e346577b008cbca06d78051bcd62aa7ea7425856ed73f09abd2b36607803055f726f52607ee7cb706b0 + languageName: node + linkType: hard + +"micromark-factory-title@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-factory-title@npm:1.0.2" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 9a9cf66babde0bad1e25d6c1087082bfde6dfc319a36cab67c89651cc1a53d0e21cdec83262b5a4c33bff49f0e3c8dc2a7bd464e991d40dbea166a8f9b37e5b2 + languageName: node + linkType: hard + +"micromark-factory-whitespace@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-whitespace@npm:1.0.0" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 0888386e6ea2dd665a5182c570d9b3d0a172d3f11694ca5a2a84e552149c9f1429f5b975ec26e1f0fa4388c55a656c9f359ce5e0603aff6175ba3e255076f20b + languageName: node + linkType: hard + +"micromark-util-character@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-character@npm:1.1.0" + dependencies: + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 504a4e3321f69bddf3fec9f0c1058239fc23336bda5be31d532b150491eda47965a251b37f8a7a9db0c65933b3aaa49cf88044fb1028be3af7c5ee6212bf8d5f + languageName: node + linkType: hard + +"micromark-util-chunked@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-chunked@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: c1efd56e8c4217bcf1c6f1a9fb9912b4a2a5503b00d031da902be922fb3fee60409ac53f11739991291357b2784fb0647ddfc74c94753a068646c0cb0fd71421 + languageName: node + linkType: hard + +"micromark-util-classify-character@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-classify-character@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 180446e6a1dec653f625ded028f244784e1db8d10ad05c5d70f08af9de393b4a03dc6cf6fa5ed8ccc9c24bbece7837abf3bf66681c0b4adf159364b7d5236dfd + languageName: node + linkType: hard + +"micromark-util-combine-extensions@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-combine-extensions@npm:1.0.0" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 5304a820ef75340e1be69d6ad167055b6ba9a3bafe8171e5945a935752f462415a9dd61eb3490220c055a8a11167209a45bfa73f278338b7d3d61fa1464d3f35 + languageName: node + linkType: hard + +"micromark-util-decode-numeric-character-reference@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-decode-numeric-character-reference@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: f3ae2bb582a80f1e9d3face026f585c0c472335c064bd850bde152376f0394cb2831746749b6be6e0160f7d73626f67d10716026c04c87f402c0dd45a1a28633 + languageName: node + linkType: hard + +"micromark-util-decode-string@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-util-decode-string@npm:1.0.2" + dependencies: + decode-named-character-reference: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-symbol: ^1.0.0 + checksum: 2dbb41c9691cc71505d39706405139fb7d6699429d577a524c7c248ac0cfd09d3dd212ad8e91c143a00b2896f26f81136edc67c5bda32d20446f0834d261b17a + languageName: node + linkType: hard + +"micromark-util-encode@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-util-encode@npm:1.0.1" + checksum: 9290583abfdc79ea3e7eb92c012c47a0e14327888f8aaa6f57ff79b3058d8e7743716b9d91abca3646f15ab3d78fdad9779fdb4ccf13349cd53309dfc845253a + languageName: node + linkType: hard + +"micromark-util-html-tag-name@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-html-tag-name@npm:1.1.0" + checksum: a9b783cec89ec813648d59799464c1950fe281ae797b2a965f98ad0167d7fa1a247718eff023b4c015f47211a172f9446b8e6b98aad50e3cd44a3337317dad2c + languageName: node + linkType: hard + +"micromark-util-normalize-identifier@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-normalize-identifier@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: d7c09d5e8318fb72f194af72664bd84a48a2928e3550b2b21c8fbc0ec22524f2a72e0f6663d2b95dc189a6957d3d7759b60716e888909710767cd557be821f8b + languageName: node + linkType: hard + +"micromark-util-resolve-all@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-resolve-all@npm:1.0.0" + dependencies: + micromark-util-types: ^1.0.0 + checksum: 409667f2bd126ef8acce009270d2aecaaa5584c5807672bc657b09e50aa91bd2e552cf41e5be1e6469244a83349cbb71daf6059b746b1c44e3f35446fef63e50 + languageName: node + linkType: hard + +"micromark-util-sanitize-uri@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-sanitize-uri@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-encode: ^1.0.0 + micromark-util-symbol: ^1.0.0 + checksum: 77448ec3a5d18f0ac975ea47591fbf0d5bd5568f9a0d033d9e318f90656031f037c5ff9137e93faf289480eaea70a5382e2571ebf9edcb1c1cd2a5187b6b3160 + languageName: node + linkType: hard + +"micromark-util-subtokenize@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-util-subtokenize@npm:1.0.2" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: c32ee58a7e1384ab1161a9ee02fbb04ad7b6e96d0b8c93dba9803c329a53d07f22ab394c7a96b2e30d6b8fbe3585b85817dba07277b1317111fc234e166bd2d1 + languageName: node + linkType: hard + +"micromark-util-symbol@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-util-symbol@npm:1.0.1" + checksum: c6a3023b3a7432c15864b5e33a1bcb5042ac7aa097f2f452e587bef45433d42d39e0a5cce12fbea91e0671098ba0c3f62a2b30ce1cde66ecbb5e8336acf4391d + languageName: node + linkType: hard + +"micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1": + version: 1.0.2 + resolution: "micromark-util-types@npm:1.0.2" + checksum: 08dc901b7c06ee3dfeb54befca05cbdab9525c1cf1c1080967c3878c9e72cb9856c7e8ff6112816e18ead36ce6f99d55aaa91560768f2f6417b415dcba1244df + languageName: node + linkType: hard + +"micromark@npm:^3.0.0": + version: 3.0.10 + resolution: "micromark@npm:3.0.10" + dependencies: + "@types/debug": ^4.0.0 + debug: ^4.0.0 + decode-named-character-reference: ^1.0.0 + micromark-core-commonmark: ^1.0.1 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-chunked: ^1.0.0 + micromark-util-combine-extensions: ^1.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-encode: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-subtokenize: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.1 + uvu: ^0.5.0 + checksum: 04663fe0308cccfbf338111b41d3d82d6445d1d2b834c9fc1880e1ea3874c4a3b81adfafe62b0bc7708ba0a86889885ea31b4dbb39f1f72190c3aab46b743bb1 + languageName: node + linkType: hard + "micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" @@ -6247,6 +6994,15 @@ __metadata: languageName: node linkType: hard +"nth-check@npm:^2.0.0": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: ^1.0.0 + checksum: 5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3 + languageName: node + linkType: hard + "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -6401,6 +7157,13 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^6.0.0": + version: 6.0.1 + resolution: "parse5@npm:6.0.1" + checksum: 7d569a176c5460897f7c8f3377eff640d54132b9be51ae8a8fa4979af940830b2b0c296ce75e5bd8f4041520aadde13170dbdec44889975f906098ea0002f4bd + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -6553,7 +7316,7 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:^1.28.0": +"prismjs@npm:^1.24.1, prismjs@npm:^1.28.0": version: 1.28.0 resolution: "prismjs@npm:1.28.0" checksum: bde93fb2beb45b7243219fc53855f59ee54b3fa179f315e8f9d66244d756ef984462e10561bbdc6713d3d7e051852472d7c284f5794a8791eeaefea2fb910b16 @@ -6595,6 +7358,13 @@ __metadata: languageName: node linkType: hard +"property-information@npm:^6.0.0": + version: 6.1.1 + resolution: "property-information@npm:6.1.1" + checksum: 654b1e5c3578e1d522bd22b7cf48881f5054789969ddbefea22e5359805fda5dbf0c5ef76bb26516da26fedac8752587ddc4c8f3b9e16bc0c6e7feb8b6086864 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -6920,6 +7690,127 @@ __metadata: languageName: node linkType: hard +"rehype-katex@npm:^6.0.2": + version: 6.0.2 + resolution: "rehype-katex@npm:6.0.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/katex": ^0.11.0 + hast-util-to-text: ^3.1.0 + katex: ^0.15.0 + rehype-parse: ^8.0.0 + unified: ^10.0.0 + unist-util-remove-position: ^4.0.0 + unist-util-visit: ^4.0.0 + checksum: ac8b3486441697b8e22cb7ebf6ec58e06d190240f45b128fe60422b9eb887599f38406581e6e3356af967eb1d45d631b0c09387f060190641f402f56c78fa771 + languageName: node + linkType: hard + +"rehype-parse@npm:^7 || ^ 8, rehype-parse@npm:^8.0.0": + version: 8.0.4 + resolution: "rehype-parse@npm:8.0.4" + dependencies: + "@types/hast": ^2.0.0 + hast-util-from-parse5: ^7.0.0 + parse5: ^6.0.0 + unified: ^10.0.0 + checksum: e678a5f9fa7cb91d5957f5f38bc37bc9fb90b8011a1ed6a90541ba6fff9f243c752c88b7f422cba8f5ba83ccb22942b1825654e8c3040970c703b85a6037efdf + languageName: node + linkType: hard + +"rehype-prism@npm:^2.1.3": + version: 2.1.3 + resolution: "rehype-prism@npm:2.1.3" + dependencies: + "@types/hast": "*" + "@types/mdast": "*" + "@types/node": ">12" + "@types/prismjs": ^1.16.6 + "@types/unist": "*" + prismjs: ^1.24.1 + rehype-parse: ^7 || ^ 8 + unist-util-is: ^4 || ^5 + unist-util-select: ^4 + unist-util-visit: ^3 || ^4 + peerDependencies: + unified: ^10 + checksum: d75864bd5b0e4b43403453fa77302956ad4b9841dd0bf201fa2258f2236c343a07306d28d465c3063e2e6eab5067467df3d24f49240acaf0c618637a33a525ab + languageName: node + linkType: hard + +"rehype-react@npm:^7.1.1": + version: 7.1.1 + resolution: "rehype-react@npm:7.1.1" + dependencies: + "@mapbox/hast-util-table-cell-style": ^0.2.0 + "@types/hast": ^2.0.0 + hast-to-hyperscript: ^10.0.0 + hast-util-whitespace: ^2.0.0 + unified: ^10.0.0 + peerDependencies: + "@types/react": ">=17" + checksum: 218b5e13776c6c0f5b430fe4c57de391f8417789b570c751f00b7c88da94ef08f7f869695189646ebc683155b02e55ac20c8e4148d4f2cbbcc39d0b05a66dca6 + languageName: node + linkType: hard + +"remark-breaks@npm:^3.0.2": + version: 3.0.2 + resolution: "remark-breaks@npm:3.0.2" + dependencies: + "@types/mdast": ^3.0.0 + unified: ^10.0.0 + unist-util-visit: ^4.0.0 + checksum: 5f46b18818f8a77e4fbc607c99736eedbe1f8cbad3d7390ce8359f08e3b749de8778ec8812287a41f51ffef2524b0be0dd623fcdbcda5de7f13f9902851f80b3 + languageName: node + linkType: hard + +"remark-gfm@npm:^3.0.1": + version: 3.0.1 + resolution: "remark-gfm@npm:3.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-gfm: ^2.0.0 + micromark-extension-gfm: ^2.0.0 + unified: ^10.0.0 + checksum: 02254f74d67b3419c2c9cf62d799ec35f6c6cd74db25c001361751991552a7ce86049a972107bff8122d85d15ae4a8d1a0618f3bc01a7df837af021ae9b2a04e + languageName: node + linkType: hard + +"remark-math@npm:^5.1.1": + version: 5.1.1 + resolution: "remark-math@npm:5.1.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-math: ^2.0.0 + micromark-extension-math: ^2.0.0 + unified: ^10.0.0 + checksum: 1baec5862e36bbb8645144a73740e63a3aed2d547a64b731bb1a0162658319679378fd70f3d3d534655c2a0fcc3f941adba31cca33808e134fa22202a5d314f9 + languageName: node + linkType: hard + +"remark-parse@npm:^10.0.1": + version: 10.0.1 + resolution: "remark-parse@npm:10.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + unified: ^10.0.0 + checksum: 505088e564ab53ff054433368adbb7b551f69240c7d9768975529837a86f1d0f085e72d6211929c5c42db315273df4afc94f3d3a8662ffdb69468534c6643d29 + languageName: node + linkType: hard + +"remark-rehype@npm:^10.1.0": + version: 10.1.0 + resolution: "remark-rehype@npm:10.1.0" + dependencies: + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + mdast-util-to-hast: ^12.1.0 + unified: ^10.0.0 + checksum: b9ac8acff3383b204dfdc2599d0bdf86e6ca7e837033209584af2e6aaa6a9013e519a379afa3201299798cab7298c8f4b388de118c312c67234c133318aec084 + languageName: node + linkType: hard + "require-from-string@npm:^2.0.2": version: 2.0.2 resolution: "require-from-string@npm:2.0.2" @@ -6995,20 +7886,20 @@ __metadata: languageName: node linkType: hard -"revolt-api@npm:0.5.3-7": - version: 0.5.3-7 - resolution: "revolt-api@npm:0.5.3-7" +"revolt-api@npm:0.5.4": + version: 0.5.4 + resolution: "revolt-api@npm:0.5.4" dependencies: "@insertish/oapi": 0.1.16 axios: ^0.26.1 lodash.defaultsdeep: ^4.6.1 - checksum: acc2f412d1be90f0cfa8a24ba715d8257813762c56ba0e48c649efff349674517036a8fa5939eda61ce31fd64a37f0c54af3d8fae56edf4596f05b10304e090e + checksum: bd40acabac1b6c5848b1d6e555297de5aa3e0950a4de67523c4cf986a8037380e3addc5e16babebc8dfa6570cd1d1957efe9a3aaa6a206b9286e5b7f5941d699 languageName: node linkType: hard -"revolt.js@npm:6.0.3": - version: 6.0.3 - resolution: "revolt.js@npm:6.0.3" +"revolt.js@npm:6.0.5": + version: 6.0.5 + resolution: "revolt.js@npm:6.0.5" dependencies: "@insertish/exponential-backoff": 3.1.0-patch.2 "@insertish/isomorphic-ws": ^4.0.1 @@ -7019,10 +7910,10 @@ __metadata: lodash.isequal: ^4.5.0 long: ^5.2.0 mobx: ^6.3.2 - revolt-api: 0.5.3-7 + revolt-api: 0.5.4 ulid: ^2.3.0 ws: ^8.2.2 - checksum: cfecbde7a9b795da75bfac3cec05f2aed5fd02cd14fcac1b2fad26285de1b887cf7ccb339e8a63a019f1e83058b041305927e029d7b5eedcc00823582d9a842a + checksum: 54d7f3a9eeafc79ae5443e22cacd85acb3359aaacccfbb11e41c8cb4a888188b200224dd3ce78847682aabbd3737d99848f6f1fbf144973cd2928d2316cffd2d languageName: node linkType: hard @@ -7111,6 +8002,15 @@ __metadata: languageName: node linkType: hard +"sade@npm:^1.7.3": + version: 1.8.1 + resolution: "sade@npm:1.8.1" + dependencies: + mri: ^1.1.0 + checksum: 0756e5b04c51ccdc8221ebffd1548d0ce5a783a44a0fa9017a026659b97d632913e78f7dca59f2496aa996a0be0b0c322afd87ca72ccd909406f49dbffa0f45d + languageName: node + linkType: hard + "safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -7457,6 +8357,13 @@ __metadata: languageName: node linkType: hard +"space-separated-tokens@npm:^2.0.0": + version: 2.0.1 + resolution: "space-separated-tokens@npm:2.0.1" + checksum: 66e30a6382d6e3ab0a6573d510235a198202071d4ebfef8c198f10433166f0cdced4dbf0946cad3c4b2ecc336896a11f98b2ec93047e140fe7aef6fd3a21365b + languageName: node + linkType: hard + "spdx-exceptions@npm:^2.1.0": version: 2.3.0 resolution: "spdx-exceptions@npm:2.3.0" @@ -7660,6 +8567,15 @@ __metadata: languageName: node linkType: hard +"style-to-object@npm:^0.3.0": + version: 0.3.0 + resolution: "style-to-object@npm:0.3.0" + dependencies: + inline-style-parser: 0.1.1 + checksum: 4d7084015207f2a606dfc10c29cb5ba569f2fe8005551df7396110dd694d6ff650f2debafa95bd5d147dfb4ca50f57868e2a7f91bf5d11ef734fe7ccbd7abf59 + languageName: node + linkType: hard + "styled-components@npm:^5.3.0": version: 5.3.1 resolution: "styled-components@npm:5.3.1" @@ -7869,6 +8785,20 @@ __metadata: languageName: node linkType: hard +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: e241da104682a0e0d807222cc1496b92e716af4db7a002f4aeff33ae6a0024fef93165d49eab11aa07c71e1347c42d46563f91dfaa4d3fb945aa535cdead53ed + languageName: node + linkType: hard + +"trough@npm:^2.0.0": + version: 2.1.0 + resolution: "trough@npm:2.1.0" + checksum: a577bb561c2b401cc0e1d9e188fcfcdf63b09b151ff56a668da12197fe97cac15e3d77d5b51f426ccfd94255744a9118e9e9935afe81a3644fa1be9783c82886 + languageName: node + linkType: hard + "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -7964,13 +8894,6 @@ __metadata: languageName: node linkType: hard -"uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": - version: 1.0.6 - resolution: "uc.micro@npm:1.0.6" - checksum: 6898bb556319a38e9cf175e3628689347bd26fec15fc6b29fa38e0045af63075ff3fea4cf1fdba9db46c9f0cbf07f2348cd8844889dd31ebd288c29fe0d27e7a - languageName: node - linkType: hard - "ulid@npm:^2.3.0": version: 2.3.0 resolution: "ulid@npm:2.3.0" @@ -8030,6 +8953,21 @@ __metadata: languageName: node linkType: hard +"unified@npm:^10.0.0, unified@npm:^10.1.2": + version: 10.1.2 + resolution: "unified@npm:10.1.2" + dependencies: + "@types/unist": ^2.0.0 + bail: ^2.0.0 + extend: ^3.0.0 + is-buffer: ^2.0.0 + is-plain-obj: ^4.0.0 + trough: ^2.0.0 + vfile: ^5.0.0 + checksum: 053e7c65ede644607f87bd625a299e4b709869d2f76ec8138569e6e886903b6988b21cd9699e471eda42bee189527be0a9dac05936f1d069a5e65d0125d5d756 + languageName: node + linkType: hard + "unique-filename@npm:^1.1.1": version: 1.1.1 resolution: "unique-filename@npm:1.1.1" @@ -8057,6 +8995,126 @@ __metadata: languageName: node linkType: hard +"unist-builder@npm:^3.0.0": + version: 3.0.0 + resolution: "unist-builder@npm:3.0.0" + dependencies: + "@types/unist": ^2.0.0 + checksum: 80459ee3c2ece90bbc4f4b4faeed524d144c1a09ee07ff3e9004648d9b71a652e80a3b3ef60311a1e92f6ab915caf27c6f08062b5f8c84fa725bc0d7c5759e84 + languageName: node + linkType: hard + +"unist-util-find-after@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-find-after@npm:4.0.0" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + checksum: 8381ef0bad18a0b1fa1c7ee47f94a2578ab6bf572eb126a1f179526b9dca47584fc070976f2d83bbe381161fa33b9164a894d0279a30ec83e65433356d43df57 + languageName: node + linkType: hard + +"unist-util-generated@npm:^2.0.0": + version: 2.0.0 + resolution: "unist-util-generated@npm:2.0.0" + checksum: 3a806793fa24a75190c217740ce706340d6cb0d51eff677134253d628f8e4355ebd8a243fe8045c583463f6bebfd50f902d653161da87c1359fcd1a14b99c8e0 + languageName: node + linkType: hard + +"unist-util-is@npm:^3.0.0": + version: 3.0.0 + resolution: "unist-util-is@npm:3.0.0" + checksum: d24a5dd80c670f763b2ae608651cf062317456aa81be51f66f45cbd7d440a2ab18356e4f48aeac6b5e3d391c69d3c3452ade5fe5aa9574bec4a2de0b10122ed5 + languageName: node + linkType: hard + +"unist-util-is@npm:^4 || ^5, unist-util-is@npm:^5.0.0": + version: 5.1.1 + resolution: "unist-util-is@npm:5.1.1" + checksum: e8743a19a304d8a8f5684f3e5ddb5546f2655847b42123687277d76566a2aba89beb7b4a8a9e9ebc4d904cd1cecc285356d7923d973a43cfc19a1e10ff6bdee4 + languageName: node + linkType: hard + +"unist-util-position@npm:^4.0.0": + version: 4.0.3 + resolution: "unist-util-position@npm:4.0.3" + dependencies: + "@types/unist": ^2.0.0 + checksum: 0d89973628d40f19345cbcc50008f7f56d411afa54434bbe6c224b22d26aaf9d4500da2de363f1f01945acab1f1c31920c514253149eb546ff9b8bbc1ea94209 + languageName: node + linkType: hard + +"unist-util-remove-position@npm:^4.0.0": + version: 4.0.1 + resolution: "unist-util-remove-position@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + unist-util-visit: ^4.0.0 + checksum: 7d2808662ac65f2b2f615822b78060419f738fb3b074b10cec77c596ea966b8f5c47553d2d322822a5975c49d2b21cdd64c198ae9fb02a9d54d1afa6342cdd6a + languageName: node + linkType: hard + +"unist-util-select@npm:^4": + version: 4.0.1 + resolution: "unist-util-select@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + css-selector-parser: ^1.0.0 + nth-check: ^2.0.0 + unist-util-is: ^5.0.0 + zwitch: ^2.0.0 + checksum: da9a69edf03d0c1a633945aae2ddf7153b4d75ff9d51085c55bf742ac0a13df327e6f2925abe863dfb6c451a8865838f28dd029cfb841356b18df56cf8872877 + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^3.0.0": + version: 3.0.2 + resolution: "unist-util-stringify-position@npm:3.0.2" + dependencies: + "@types/unist": ^2.0.0 + checksum: 2dfd7a0fb2a55e99cc319c3bf7f9f1f73ed652978fa70d19117faa7245d20f21738ec926ecc47f341705ca1bb157e87ced0b6bb5ecaa666bd2ae6b2510d6a671 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^2.0.0": + version: 2.1.2 + resolution: "unist-util-visit-parents@npm:2.1.2" + dependencies: + unist-util-is: ^3.0.0 + checksum: 048edbb590a8c4bc0043eec9f50d3fe76faa58f1ac663a7e6dee5e895ddd0ce8bc52f2cfe2e633849fa93671e8de021070667acb1518e3d40220768c7f70a3d3 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^5.0.0": + version: 5.1.0 + resolution: "unist-util-visit-parents@npm:5.1.0" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + checksum: 7c413dbb3dfcb679109fa8f0965d9abf117c3c53fa7b8823f68cac0ea53adbe98c1ce954d36c034e086c966b48b1d44d42c85f7bf6b42a032f728ac338929513 + languageName: node + linkType: hard + +"unist-util-visit@npm:^1.4.1": + version: 1.4.1 + resolution: "unist-util-visit@npm:1.4.1" + dependencies: + unist-util-visit-parents: ^2.0.0 + checksum: e9395205b6908c8d0fe71bc44e65d89d4781d1bb2d453a33cb67ed4124bad0b89d6b1d526ebaecb82a7c48e211bdf6f24351449b8cc115327b345f4617c18728 + languageName: node + linkType: hard + +"unist-util-visit@npm:^3 || ^4, unist-util-visit@npm:^4.0.0, unist-util-visit@npm:^4.1.0": + version: 4.1.0 + resolution: "unist-util-visit@npm:4.1.0" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^5.0.0 + checksum: 3521abee2ed4535092aac073d05f46255475c89781b8e9d8c951a473d91b5d6e4d5912ae4a68a4c1cf17a42ed0108cb93103c7f5c736977529969997451363fb + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.0 resolution: "universalify@npm:2.0.0" @@ -8117,6 +9175,20 @@ __metadata: languageName: node linkType: hard +"uvu@npm:^0.5.0": + version: 0.5.6 + resolution: "uvu@npm:0.5.6" + dependencies: + dequal: ^2.0.0 + diff: ^5.0.0 + kleur: ^4.0.3 + sade: ^1.7.3 + bin: + uvu: bin.js + checksum: 09460a37975627de9fcad396e5078fb844d01aaf64a6399ebfcfd9e55f1c2037539b47611e8631f89be07656962af0cf48c334993db82b9ae9c3d25ce3862168 + languageName: node + linkType: hard + "v8-compile-cache@npm:^2.0.3": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0" @@ -8131,6 +9203,38 @@ __metadata: languageName: node linkType: hard +"vfile-location@npm:^4.0.0": + version: 4.0.1 + resolution: "vfile-location@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + vfile: ^5.0.0 + checksum: cc0df62075c741beee699e651374aeb56c4c1f4333398c0ba924281c2b51d4b7669c69c5b837ea395775626ad030d6f1bd27fd0a7eaf3f9f1bbd55393948ad6c + languageName: node + linkType: hard + +"vfile-message@npm:^3.0.0": + version: 3.1.2 + resolution: "vfile-message@npm:3.1.2" + dependencies: + "@types/unist": ^2.0.0 + unist-util-stringify-position: ^3.0.0 + checksum: 96fbd9e9b5e0babb5ee61e3a716dc7a6a8c28f2c8c711837d95c88b782161b31549ad16059a78990d7b836d0f4d3b4d8c9ffde44370d48d9cac991fc1e3e17c5 + languageName: node + linkType: hard + +"vfile@npm:^5.0.0": + version: 5.3.4 + resolution: "vfile@npm:5.3.4" + dependencies: + "@types/unist": ^2.0.0 + is-buffer: ^2.0.0 + unist-util-stringify-position: ^3.0.0 + vfile-message: ^3.0.0 + checksum: 2382edc7c6e3502bca72bc95bc1ff0fe1852482e8a0ac257615f9ab12f32564d6f6a55da8756b74a900d26a247da5ca23a92ca7c9a18dbda2b0f87504ef0611f + languageName: node + linkType: hard + "vite-plugin-pwa@npm:^0.11.13": version: 0.11.13 resolution: "vite-plugin-pwa@npm:0.11.13" @@ -8178,6 +9282,13 @@ __metadata: languageName: node linkType: hard +"web-namespaces@npm:^2.0.0": + version: 2.0.1 + resolution: "web-namespaces@npm:2.0.1" + checksum: b6d9f02f1a43d0ef0848a812d89c83801d5bbad57d8bb61f02eb6d7eb794c3736f6cc2e1191664bb26136594c8218ac609f4069722c6f56d9fc2d808fa9271c6 + languageName: node + linkType: hard + "webidl-conversions@npm:^4.0.2": version: 4.0.2 resolution: "webidl-conversions@npm:4.0.2" @@ -8512,3 +9623,10 @@ __metadata: checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 languageName: node linkType: hard + +"zwitch@npm:^2.0.0": + version: 2.0.2 + resolution: "zwitch@npm:2.0.2" + checksum: 8edd7af8375f12f64d8dbef815af32cd77bd9237d0b013210ba4e3aef25fdc460fe264cd0a19deabe9f86ef0c607240ebac1a336bf4a70bf06ef53e0652de116 + languageName: node + linkType: hard From 4a85dd69cf66dcbf654cd67ba11db80c033ea90e Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Fri, 8 Jul 2022 15:19:16 +0100 Subject: [PATCH 2/5] fix: limit maximum quote depth to 3 --- src/components/markdown/Markdown.tsx | 4 ++- src/components/markdown/RemarkRenderer.tsx | 32 ++++++++++++++++------ src/components/markdown/hast.ts | 7 +++++ 3 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 src/components/markdown/hast.ts diff --git a/src/components/markdown/Markdown.tsx b/src/components/markdown/Markdown.tsx index 9ff974fb..77c61dc9 100644 --- a/src/components/markdown/Markdown.tsx +++ b/src/components/markdown/Markdown.tsx @@ -3,11 +3,13 @@ import { Suspense, lazy } from "preact/compat"; const Renderer = lazy(() => import("./RemarkRenderer")); export interface MarkdownProps { - content?: string | null; + content: string; disallowBigEmoji?: boolean; } export default function Markdown(props: MarkdownProps) { + if (!props.content) return null; + return ( // @ts-expect-error Typings mis-match. diff --git a/src/components/markdown/RemarkRenderer.tsx b/src/components/markdown/RemarkRenderer.tsx index 06d1c3f1..9501a66f 100644 --- a/src/components/markdown/RemarkRenderer.tsx +++ b/src/components/markdown/RemarkRenderer.tsx @@ -15,14 +15,14 @@ import { memo } from "preact/compat"; import { useEffect, useMemo, useState } from "preact/hooks"; import { MarkdownProps } from "./Markdown"; +import { handlers } from "./hast"; import { RenderCodeblock } from "./plugins/Codeblock"; import { RenderAnchor } from "./plugins/anchors"; import { remarkChannels, RenderChannel } from "./plugins/channels"; import { isOnlyEmoji, remarkEmoji, RenderEmoji } from "./plugins/emoji"; import { remarkMention, RenderMention } from "./plugins/mentions"; -import { passThroughComponents } from "./plugins/remarkRegexComponent"; import { remarkSpoiler, RenderSpoiler } from "./plugins/spoiler"; -import { remarkTimestamps, timestampHandler } from "./plugins/timestamps"; +import { remarkTimestamps } from "./plugins/timestamps"; import "./prism"; /** @@ -139,10 +139,7 @@ const render = unified() .use(remarkEmoji) .use(remarkMention) .use(remarkRehype, { - handlers: { - ...passThroughComponents("emoji", "spoiler", "mention", "channel"), - timestamp: timestampHandler, - }, + handlers, }) .use(rehypeKatex, { maxSize: 10, @@ -173,15 +170,34 @@ const Container = styled.div<{ largeEmoji: boolean }>` --emoji-size: ${(props) => (props.largeEmoji ? "3em" : "1.25em")}; `; +/** + * Regex for matching execessive blockquotes + */ +const RE_QUOTE = /(^[>\s][>\s])[>\s]+([^]+)$/gm; + +/** + * Sanitise Markdown input before rendering + * @param content Input string + * @returns Sanitised string + */ +function sanitise(content: string) { + // Strip excessive blockquote indentation + return content.replace(RE_QUOTE, (_, m0, m1) => m0 + m1); +} + /** * Remark renderer component */ export default memo(({ content, disallowBigEmoji }: MarkdownProps) => { + const sanitisedContent = useMemo(() => sanitise(content), [content]); + const [Content, setContent] = useState(null!); useEffect(() => { - render.process(content!).then((file) => setContent(file.result)); - }, [content]); + render + .process(sanitisedContent) + .then((file) => setContent(file.result)); + }, [sanitisedContent]); const largeEmoji = useMemo( () => !disallowBigEmoji && isOnlyEmoji(content!), diff --git a/src/components/markdown/hast.ts b/src/components/markdown/hast.ts new file mode 100644 index 00000000..d2054aff --- /dev/null +++ b/src/components/markdown/hast.ts @@ -0,0 +1,7 @@ +import { passThroughComponents } from "./plugins/remarkRegexComponent"; +import { timestampHandler } from "./plugins/timestamps"; + +export const handlers = { + ...passThroughComponents("emoji", "spoiler", "mention", "channel"), + timestamp: timestampHandler, +}; From 262b931810e874623977c2c8d0c6a9464fd1991d Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Fri, 8 Jul 2022 15:36:18 +0100 Subject: [PATCH 3/5] fix: actually render HTML out instead of obliterating it --- src/components/markdown/RemarkRenderer.tsx | 18 +++++++++++++++--- src/components/markdown/plugins/mentions.tsx | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/markdown/RemarkRenderer.tsx b/src/components/markdown/RemarkRenderer.tsx index 9501a66f..05025ff1 100644 --- a/src/components/markdown/RemarkRenderer.tsx +++ b/src/components/markdown/RemarkRenderer.tsx @@ -173,7 +173,12 @@ const Container = styled.div<{ largeEmoji: boolean }>` /** * Regex for matching execessive blockquotes */ -const RE_QUOTE = /(^[>\s][>\s])[>\s]+([^]+)$/gm; +const RE_QUOTE = /(^[>\s][>\s])[>\s]+([^]+$)/gm; + +/** + * Regex for matching open angled bracket + */ +const RE_OPEN_BRACKET = /\s][>\s])[>\s]+([^]+)$/gm; * @returns Sanitised string */ function sanitise(content: string) { - // Strip excessive blockquote indentation - return content.replace(RE_QUOTE, (_, m0, m1) => m0 + m1); + return ( + content + // Strip excessive blockquote indentation + .replace(RE_QUOTE, (_, m0, m1) => m0 + m1) + // Map < to HTML entity LT + // (otherwise all HTML is just obliterated, + // not even displayed as plain text) + .replace(RE_OPEN_BRACKET, "<") + ); } /** diff --git a/src/components/markdown/plugins/mentions.tsx b/src/components/markdown/plugins/mentions.tsx index 1411dcf5..917d8cda 100644 --- a/src/components/markdown/plugins/mentions.tsx +++ b/src/components/markdown/plugins/mentions.tsx @@ -12,6 +12,8 @@ const Mention = styled.a` align-items: center; display: inline-flex; + cursor: pointer; + font-weight: 600; background: var(--secondary-background); border-radius: calc(var(--border-radius) * 2); From 7e20d5029e76d769a7503633014ac39dd1cd63fb Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Fri, 8 Jul 2022 15:45:16 +0100 Subject: [PATCH 4/5] fix: underline anchor; prevent jitter on render --- src/components/markdown/RemarkRenderer.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/markdown/RemarkRenderer.tsx b/src/components/markdown/RemarkRenderer.tsx index 05025ff1..26f03b37 100644 --- a/src/components/markdown/RemarkRenderer.tsx +++ b/src/components/markdown/RemarkRenderer.tsx @@ -12,7 +12,7 @@ import { unified } from "unified"; import { createElement } from "preact"; import { memo } from "preact/compat"; -import { useEffect, useMemo, useState } from "preact/hooks"; +import { useLayoutEffect, useMemo, useState } from "preact/hooks"; import { MarkdownProps } from "./Markdown"; import { handlers } from "./hast"; @@ -163,11 +163,18 @@ const render = unified() * Markdown parent container */ const Container = styled.div<{ largeEmoji: boolean }>` + // Allow scrolling block math .math-display { overflow-x: auto; } + // Set emoji size --emoji-size: ${(props) => (props.largeEmoji ? "3em" : "1.25em")}; + + // Underline link hover + a:hover { + text-decoration: underline; + } `; /** @@ -205,7 +212,7 @@ export default memo(({ content, disallowBigEmoji }: MarkdownProps) => { const [Content, setContent] = useState(null!); - useEffect(() => { + useLayoutEffect(() => { render .process(sanitisedContent) .then((file) => setContent(file.result)); From c12d40d0da0212d26039eaefd3c804e9511108f3 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Fri, 8 Jul 2022 16:58:21 +0100 Subject: [PATCH 5/5] fix: correct mention styling --- src/components/markdown/plugins/mentions.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/markdown/plugins/mentions.tsx b/src/components/markdown/plugins/mentions.tsx index 917d8cda..eae6938b 100644 --- a/src/components/markdown/plugins/mentions.tsx +++ b/src/components/markdown/plugins/mentions.tsx @@ -7,19 +7,28 @@ import { createComponent, CustomComponentProps } from "./remarkRegexComponent"; const Mention = styled.a` gap: 4px; - padding: 0 6px; flex-shrink: 0; + padding-left: 2px; + padding-right: 6px; align-items: center; display: inline-flex; + vertical-align: middle; cursor: pointer; font-weight: 600; + text-decoration: none !important; background: var(--secondary-background); border-radius: calc(var(--border-radius) * 2); + transition: 0.1s ease filter; + &:hover { - text-decoration: none; + filter: brightness(0.75); + } + + &:active { + filter: brightness(0.65); } svg {