diff --git a/apps/app/package.json b/apps/app/package.json index d49e8519..26eb95a6 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -15,6 +15,7 @@ "@mx/config": "workspace:*", "@total-typescript/ts-reset": "^0.5.1", "@types/eslint": "~8.4.6", + "@types/mdast": "^4.0.3", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "autoprefixer": "^10.4.16", @@ -57,7 +58,11 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-icons": "^4.10.1", + "remark-gfm": "^4.0.0", + "remark-parse": "^11.0.0", "tailwind-merge": "^2.1.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0", "urlpattern-polyfill": "^10.0.0", "use-callback-ref": "^1.3.1", "zustand": "^4.4.7" diff --git a/apps/app/src/media-note/note-index/playlist.ts b/apps/app/src/media-note/note-index/playlist.ts new file mode 100644 index 00000000..54067530 --- /dev/null +++ b/apps/app/src/media-note/note-index/playlist.ts @@ -0,0 +1,309 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Component, TFolder, TFile, parseLinktext, debounce } from "obsidian"; +import type { + MetadataCache, + CachedMetadata, + Pos, + LinkCache, + ListItemCache, +} from "obsidian"; +import { getFileMediaInfo, isFileMediaInfo } from "@/media-view/media-info"; +import type { FileMediaInfo, MediaInfo } from "@/media-view/media-info"; +import type MxPlugin from "@/mx-main"; +import type { MediaType } from "@/patch/media-type"; +import { fromFile } from "@/web/url-match"; +import { + extractFirstMarkdownLink, + extractListItemMainText, + parseMarkdown, +} from "./syntax"; + +export interface Playlist { + title: string; + list: PlaylistItem[]; +} +export interface PlaylistItem { + media: MediaInfo | null; + title: string; + type: MediaTaskSymbolType; + /** + * Index of the parent item in the list + */ + parent: number; +} + +const emptyLists: Playlist[] = []; + +declare module "obsidian" { + interface MetadataCache { + on( + name: "mx-playlist-change", + callback: (index: PlaylistIndex) => void, + ): EventRef; + trigger(name: "mx-playlist-change", index: PlaylistIndex): void; + } +} + +function extractText(text: string, range: Pos) { + return text.slice(range.start.offset, range.end.offset); +} + +export class PlaylistIndex extends Component { + app; + constructor(public plugin: MxPlugin) { + super(); + this.app = plugin.app; + } + + /** + * @param media MediaURL.jsonState.source + */ + get(media: string | undefined) { + if (!media) return emptyLists; + return this.mediaToPlaylistIndex.get(media) ?? emptyLists; + } + + /** + * immutable update playlist array holding the media + * to make reactivity work + */ + private mediaToPlaylistIndex = new Map(); + private listFileCache = new Map(); + + private onResolve() { + this.mediaToPlaylistIndex.clear(); + for (const { file, playlist } of iteratePlaylist(this.plugin)) { + this.requestUpdate(file, playlist); + } + this.registerEvent( + this.app.metadataCache.on("changed", (file) => { + this.requestUpdate(file, getPlaylistMeta(file, this.plugin)); + }), + ); + this.registerEvent( + this.app.metadataCache.on("deleted", (file) => { + this.remove(file.path); + }), + ); + this.registerEvent( + this.app.vault.on("rename", (file, oldPath) => { + if (!(file instanceof TFile)) return; + const prev = this.listFileCache.get(oldPath); + if (!prev) return; + this.remove(oldPath); + this.update(file, prev); + }), + ); + } + + remove(listFilePath: string) { + const prevPlaylist = this.listFileCache.get(listFilePath); + if (!prevPlaylist) return; + this.listFileCache.delete(listFilePath); + // clear old data + for (const media of this.mediaToPlaylistIndex.keys()) { + const prev = this.mediaToPlaylistIndex.get(media)!; + const next = prev.filter((playlist) => playlist !== prevPlaylist); + if (next.length === 0) { + this.mediaToPlaylistIndex.delete(media); + } else { + this.mediaToPlaylistIndex.set(media, next); + } + } + this.requestNotify(); + } + + updateQueue = new WeakMap(); + updater = debounce((listFile: TFile) => { + const data = this.updateQueue.get(listFile); + if (!data) return; + this.update(listFile, data); + this.updateQueue.delete(listFile); + }, 500); + requestUpdate(listFile: TFile, data: Promise) { + data.then((playlist) => { + if (!playlist) return; + this.updateQueue.set(listFile, playlist); + this.updater(listFile); + }); + } + + notify() { + this.app.metadataCache.trigger("mx-playlist-change", this); + } + requestNotify = debounce(() => this.notify(), 500); + + update(listFile: TFile, data: Playlist) { + this.remove(listFile.path); + // make sure only one instance of the playlist is stored + const uniqCache = new Set(); + data.list.forEach((item) => { + if (!item.media) return; + const key = ( + isFileMediaInfo(item.media) + ? fromFile(item.media.file, this.app.vault) + : item.media + ).jsonState.source; + if (uniqCache.has(key)) return; + this.mediaToPlaylistIndex.set(key, [ + ...(this.mediaToPlaylistIndex.get(key) ?? []), + data, + ]); + uniqCache.add(key); + }); + this.requestNotify(); + } + + onload(): void { + waitUntilResolve(this.app.metadataCache, this).then(() => { + this.onResolve(); + }); + } +} + +function waitUntilResolve( + meta: MetadataCache, + component?: Component, +): Promise { + if (meta.initialized) return Promise.resolve(); + return new Promise((resolve) => { + const evt = meta.on("initialized", () => { + meta.offref(evt); + resolve(); + }); + component?.registerEvent(evt); + }); +} +function* iterateFiles(folder: TFolder): IterableIterator { + for (const child of folder.children) { + if (child instanceof TFolder) { + yield* iterateFiles(child); + } else if (child instanceof TFile) { + yield child; + } + } +} + +function* iteratePlaylist(plugin: MxPlugin) { + const { vault } = plugin.app; + for (const file of iterateFiles(vault.getRoot())) { + if (file.extension !== "md") continue; + const playlist = getPlaylistMeta(file, plugin); + if (!playlist) continue; + yield { playlist, file }; + } +} + +async function getPlaylistMeta( + file: TFile, + plugin: MxPlugin, +): Promise { + const meta = plugin.app.metadataCache.getFileCache(file); + if (!meta) return null; + const ctx = { source: file, plugin }; + const list = await parsePlaylist(meta, ctx); + if (!list) return null; + return { title: getFileTitle(meta, file), list }; +} + +async function parsePlaylist( + meta: CachedMetadata, + ctx: { + source: TFile; + plugin: MxPlugin; + }, +): Promise { + const { metadataCache, vault } = ctx.plugin.app; + const { frontmatter } = meta; + if (frontmatter?.playlist !== true || !meta.sections || !meta.listItems) + return null; + + const listSection = meta.sections.find((s) => s.type === "list"); + if (!listSection) return []; + const withinListSection = (item: { position: Pos }) => + within(item, listSection); + const listItems = meta.listItems.filter(withinListSection); + if (!isPlaylistItems(listItems)) return []; + + const links = meta.links?.filter(withinListSection) ?? []; + const fileContent = await vault.cachedRead(ctx.source); + const playlist = listItems.map((listItem, i, arr): PlaylistItem => { + const internalLinkIdx = links.findIndex((link) => within(link, listItem)); + const { parent: _parent, task } = listItem; + const type = task && taskSymbolMediaTypeMap[task]; + + // convert start_line-based indexing to array_index-based indexing + const parent = + _parent >= 0 + ? arr.findIndex((li) => li.position.start.line === _parent) + : -1; + if (internalLinkIdx !== -1) { + // remove all links within the list item + const internalLink = links[internalLinkIdx]; + const last = links.findLastIndex((link) => within(link, listItem)); + links.splice(internalLinkIdx, last - internalLinkIdx + 1); + const media = getMediaInfo(internalLink); + return { + media, + type: type ?? "generic", + parent, + title: internalLink.displayText ?? "", + }; + } + // handle external links + const text = extractText(fileContent, listItem.position); + const syntax = parseMarkdown(text); + const externalLink = extractFirstMarkdownLink(text, syntax); + if (externalLink) { + const { display, url } = externalLink; + const media = ctx.plugin.resolveUrl(url); + return { media, title: display, type: type || "generic", parent }; + } + return { + media: null, + title: extractListItemMainText(text, syntax) || "Item", + type: type || "chapter", + parent, + }; + }); + return playlist; + + function getMediaInfo({ link }: LinkCache): FileMediaInfo | null { + const { path, subpath } = parseLinktext(link); + const file = metadataCache.getFirstLinkpathDest(path, ctx.source.path); + return getFileMediaInfo(file, subpath); + } +} + +const taskSymbolMediaTypeMap = { + ">": "video", + "^": "audio", + _: "subtitle", + "#": "generic", + "/": "chapter", +} satisfies Record; +type MediaTaskSymbol = keyof typeof taskSymbolMediaTypeMap; +type MediaTaskSymbolType = (typeof taskSymbolMediaTypeMap)[MediaTaskSymbol]; + +function isPlaylistItems( + list: ListItemCache[], +): list is (ListItemCache & { task?: MediaTaskSymbol })[] { + return list.some( + (i) => i.task && taskSymbolMediaTypeMap[i.task as MediaTaskSymbol], + ); +} + +function getFileTitle(meta: CachedMetadata, file: TFile): string { + return ( + meta.frontmatter?.title?.trim() || + meta.headings?.find((h) => h.level === 1)?.heading?.trim() || + file.basename.trim() + ); +} + +function within(item: { position: Pos }, parent: { position: Pos }) { + return ( + item.position.start.offset >= parent.position.start.offset && + item.position.end.offset <= parent.position.end.offset + ); +} diff --git a/apps/app/src/media-note/note-index/syntax.ts b/apps/app/src/media-note/note-index/syntax.ts new file mode 100644 index 00000000..7c755265 --- /dev/null +++ b/apps/app/src/media-note/note-index/syntax.ts @@ -0,0 +1,100 @@ +import type { Root, Link, ListItem } from "mdast"; +import remarkGfm from "remark-gfm"; +import remarkParse from "remark-parse"; +import { unified } from "unified"; +import { visit, EXIT } from "unist-util-visit"; + +export function parseMarkdown(text: string): Root { + return unified().use(remarkParse).use(remarkGfm).parse(text); +} + +export function extractFirstMarkdownLink(text: string, tree: Root) { + let link: Link | undefined; + visit(tree, "link", (node: Link) => { + link = node; + return EXIT; + }); + if (!link) return null; + const displayTextStart = link.children.first()?.position?.start.offset, + displayTextEnd = link.children.last()?.position?.end.offset; + return { + display: + displayTextStart && displayTextEnd + ? text.slice(displayTextStart, displayTextEnd) + : "", + url: link.url, + title: link.title, + }; +} + +/** + * @returns raw markdown text + */ +export function extractListItemMainText(text: string, tree: Root) { + let mainText = ""; + visit(tree, "listItem", (node: ListItem) => { + const start = node.children.first()?.position?.start.offset, + end = node.children.last()?.position?.end.offset; + if (start && end) { + mainText = text.slice(start, end); + } + return EXIT; + }); + return mainText; +} + +// function _extractFirstMarkdownLink( +// input: string, +// ): { display: string; link: string } | null { +// let state: "start" | "display" | "link" | "url" = "start"; +// let display = ""; +// let link = ""; +// let escape = false; + +// for (const char of input) { +// switch (state) { +// case "start": +// if (char === "[") { +// state = "display"; +// } +// break; +// case "display": +// if (char === "\\" && !escape) { +// escape = true; +// } else if (char === "]" && !escape) { +// state = "link"; +// } else { +// display += char; +// escape = false; +// } +// break; +// case "link": +// if (char === "(") { +// state = "url"; +// } else { +// // Reset if not a valid link +// state = "start"; +// display = ""; +// link = ""; +// } +// break; +// case "url": +// if (char === "\\" && !escape) { +// escape = true; +// } else if (char === ")" && !escape) { +// if (!/\s/.test(link.trim())) +// return { display: display.trim(), link: link.trim() }; // Return the first valid link +// // reset if not a valid link +// state = "start"; +// display = ""; +// link = ""; +// } else { +// link += char; +// escape = false; +// } +// break; +// } +// } + +// return null; // No valid link found +// } diff --git a/apps/app/src/media-view/file-embed.tsx b/apps/app/src/media-view/file-embed.tsx index e6882b3a..8ce82e81 100644 --- a/apps/app/src/media-view/file-embed.tsx +++ b/apps/app/src/media-view/file-embed.tsx @@ -4,9 +4,8 @@ import ReactDOM from "react-dom/client"; import { MediaViewContext, createMediaViewStore } from "@/components/context"; import { Player } from "@/components/player"; import { dataLpPassthrough } from "@/components/player/buttons"; -import type { FileMediaInfo } from "@/media-view/media-info"; +import { getFileMediaInfo, type FileMediaInfo } from "@/media-view/media-info"; import type MxPlugin from "@/mx-main"; -import { checkMediaType } from "@/patch/media-type"; import { type PlayerComponent } from "./base"; export class MediaFileEmbed @@ -41,14 +40,7 @@ export class MediaFileEmbed } getMediaInfo(): FileMediaInfo | null { - if (!this.file) return null; - const type = checkMediaType(this.file.extension); - if (!type) return null; - return { - type, - file: this.file, - hash: this.subpath, - }; + return getFileMediaInfo(this.file, this.subpath); } // eslint-disable-next-line react/require-render-return diff --git a/apps/app/src/media-view/media-info.ts b/apps/app/src/media-view/media-info.ts index ee7480dc..eaaf45d8 100644 --- a/apps/app/src/media-view/media-info.ts +++ b/apps/app/src/media-view/media-info.ts @@ -15,6 +15,16 @@ export function isFileMediaInfo(info: unknown): info is FileMediaInfo { return (info as FileMediaInfo).file instanceof TFile; } +export function getFileMediaInfo( + file: TFile | null, + hash: string, +): FileMediaInfo | null { + if (!file) return null; + const type = checkMediaType(file.extension); + if (!type) return null; + return { type, file, hash }; +} + export function parseFileInfo( filePath: string, hash: string, diff --git a/apps/app/src/mx-main.ts b/apps/app/src/mx-main.ts index 5115d0e2..f6facb3d 100644 --- a/apps/app/src/mx-main.ts +++ b/apps/app/src/mx-main.ts @@ -14,6 +14,7 @@ import { onInternalLinkClick, } from "./media-note/link-click"; import { MediaNoteIndex } from "./media-note/note-index"; +import { PlaylistIndex } from "./media-note/note-index/playlist"; import { MediaFileEmbed } from "./media-view/file-embed"; import { AudioFileView, VideoFileView } from "./media-view/file-view"; import { MediaEmbedView } from "./media-view/iframe-view"; @@ -92,6 +93,7 @@ export default class MxPlugin extends Plugin { } mediaNote = this.addChild(new MediaNoteIndex(this)); + playlist = this.addChild(new PlaylistIndex(this)); biliReq = this.addChild(new BilibiliRequestHacker(this)); leafOpener = this.addChild(new LeafOpener(this)); handleMediaNote = handleMediaNote; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8de0b8df..98846405 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,9 +74,21 @@ importers: react-icons: specifier: ^4.10.1 version: 4.12.0(react@18.2.0) + remark-gfm: + specifier: ^4.0.0 + version: 4.0.0 + remark-parse: + specifier: ^11.0.0 + version: 11.0.0 tailwind-merge: specifier: ^2.1.0 version: 2.2.1 + unified: + specifier: ^11.0.4 + version: 11.0.4 + unist-util-visit: + specifier: ^5.0.0 + version: 5.0.0 urlpattern-polyfill: specifier: ^10.0.0 version: 10.0.0 @@ -108,6 +120,9 @@ importers: '@types/eslint': specifier: ~8.4.6 version: 8.4.10 + '@types/mdast': + specifier: ^4.0.3 + version: 4.0.3 '@types/react': specifier: ^18.0.0 version: 18.2.48 @@ -2683,7 +2698,6 @@ packages: resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} dependencies: '@types/unist': 3.0.2 - dev: false /@types/mdx@2.0.11: resolution: {integrity: sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==} @@ -2788,7 +2802,6 @@ packages: /@types/unist@3.0.2: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - dev: false /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -6874,6 +6887,15 @@ packages: unist-util-visit-parents: 5.1.3 dev: false + /mdast-util-find-and-replace@3.0.1: + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + dependencies: + '@types/mdast': 4.0.3 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + dev: false + /mdast-util-from-markdown@1.3.1: resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} dependencies: @@ -6893,6 +6915,25 @@ packages: - supports-color dev: false + /mdast-util-from-markdown@2.0.0: + resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-decode-string: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-gfm-autolink-literal@1.0.3: resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} dependencies: @@ -6902,6 +6943,16 @@ packages: micromark-util-character: 1.2.0 dev: false + /mdast-util-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + dependencies: + '@types/mdast': 4.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.1.0 + dev: false + /mdast-util-gfm-footnote@1.0.2: resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} dependencies: @@ -6910,6 +6961,18 @@ packages: micromark-util-normalize-identifier: 1.1.0 dev: false + /mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-gfm-strikethrough@1.0.3: resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} dependencies: @@ -6917,6 +6980,16 @@ packages: mdast-util-to-markdown: 1.5.0 dev: false + /mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-gfm-table@1.0.7: resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} dependencies: @@ -6928,6 +7001,18 @@ packages: - supports-color dev: false + /mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + markdown-table: 3.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-gfm-task-list-item@1.0.2: resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} dependencies: @@ -6935,6 +7020,17 @@ packages: mdast-util-to-markdown: 1.5.0 dev: false + /mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-gfm@2.0.2: resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} dependencies: @@ -6949,6 +7045,20 @@ packages: - supports-color dev: false + /mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + dependencies: + mdast-util-from-markdown: 2.0.0 + mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-math@2.0.2: resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} dependencies: @@ -7019,6 +7129,13 @@ packages: unist-util-is: 5.2.1 dev: false + /mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + dependencies: + '@types/mdast': 4.0.3 + unist-util-is: 6.0.0 + dev: false + /mdast-util-to-hast@12.3.0: resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} dependencies: @@ -7059,12 +7176,31 @@ packages: zwitch: 2.0.4 dev: false + /mdast-util-to-markdown@2.1.0: + resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + dev: false + /mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} dependencies: '@types/mdast': 3.0.15 dev: false + /mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + dependencies: + '@types/mdast': 4.0.3 + dev: false + /media-captions@1.0.2: resolution: {integrity: sha512-QxAFc+XTGZeMx+ZvLtMxEDgAjd0kr1LJ2NekLr1cw/UKENOxK7B9g6HwtuTQzCXxmb4Dknd4T8M7FOqqCK9buA==} engines: {node: '>=16'} @@ -7137,6 +7273,27 @@ packages: uvu: 0.5.6 dev: false + /micromark-core-commonmark@2.0.0: + resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.0 + micromark-factory-label: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-factory-title: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-html-tag-name: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-autolink-literal@1.0.5: resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} dependencies: @@ -7146,6 +7303,15 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-extension-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-footnote@1.1.2: resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} dependencies: @@ -7159,6 +7325,19 @@ packages: uvu: 0.5.6 dev: false + /micromark-extension-gfm-footnote@2.0.0: + resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-strikethrough@1.0.7: resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} dependencies: @@ -7170,6 +7349,17 @@ packages: uvu: 0.5.6 dev: false + /micromark-extension-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-table@1.0.7: resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} dependencies: @@ -7180,12 +7370,28 @@ packages: uvu: 0.5.6 dev: false + /micromark-extension-gfm-table@2.0.0: + resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-tagfilter@1.0.2: resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} dependencies: micromark-util-types: 1.1.0 dev: false + /micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + dependencies: + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm-task-list-item@1.0.5: resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} dependencies: @@ -7196,6 +7402,16 @@ packages: uvu: 0.5.6 dev: false + /micromark-extension-gfm-task-list-item@2.0.1: + resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-gfm@2.0.3: resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} dependencies: @@ -7209,6 +7425,19 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + dependencies: + micromark-extension-gfm-autolink-literal: 2.0.0 + micromark-extension-gfm-footnote: 2.0.0 + micromark-extension-gfm-strikethrough: 2.0.0 + micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.0.1 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-extension-math@2.1.2: resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} dependencies: @@ -7290,6 +7519,14 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-factory-label@1.1.0: resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} dependencies: @@ -7299,6 +7536,15 @@ packages: uvu: 0.5.6 dev: false + /micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-factory-mdx-expression@1.0.9: resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==} dependencies: @@ -7319,6 +7565,13 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-factory-title@1.1.0: resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} dependencies: @@ -7328,6 +7581,15 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-factory-whitespace@1.1.0: resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} dependencies: @@ -7337,6 +7599,15 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-util-character@1.2.0: resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} dependencies: @@ -7357,6 +7628,12 @@ packages: micromark-util-symbol: 1.1.0 dev: false + /micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: false + /micromark-util-classify-character@1.1.0: resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} dependencies: @@ -7365,6 +7642,14 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-util-combine-extensions@1.1.0: resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} dependencies: @@ -7372,12 +7657,25 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-util-decode-numeric-character-reference@1.1.0: resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} dependencies: micromark-util-symbol: 1.1.0 dev: false + /micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: false + /micromark-util-decode-string@1.1.0: resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} dependencies: @@ -7387,6 +7685,15 @@ packages: micromark-util-symbol: 1.1.0 dev: false + /micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + dev: false + /micromark-util-encode@1.1.0: resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} dev: false @@ -7412,18 +7719,34 @@ packages: resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} dev: false + /micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + dev: false + /micromark-util-normalize-identifier@1.1.0: resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} dependencies: micromark-util-symbol: 1.1.0 dev: false + /micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: false + /micromark-util-resolve-all@1.1.0: resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} dependencies: micromark-util-types: 1.1.0 dev: false + /micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + dependencies: + micromark-util-types: 2.0.0 + dev: false + /micromark-util-sanitize-uri@1.2.0: resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} dependencies: @@ -7449,6 +7772,15 @@ packages: uvu: 0.5.6 dev: false + /micromark-util-subtokenize@2.0.0: + resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-util-symbol@1.1.0: resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} dev: false @@ -7489,6 +7821,30 @@ packages: - supports-color dev: false + /micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.4(supports-color@8.1.1) + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -8919,6 +9275,19 @@ packages: - supports-color dev: false + /remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + /remark-math@5.1.1: resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} dependencies: @@ -8947,6 +9316,17 @@ packages: - supports-color dev: false + /remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + micromark-util-types: 2.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + /remark-reading-time@2.0.1: resolution: {integrity: sha512-fy4BKy9SRhtYbEHvp6AItbRTnrhiDGbqLQTSYVbQPGuRCncU1ubSsh9p/W5QZSxtYcUXv8KGL0xBgPLyNJA1xw==} dependencies: @@ -8965,6 +9345,14 @@ packages: unified: 10.1.2 dev: false + /remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-to-markdown: 2.1.0 + unified: 11.0.4 + dev: false + /remove-accents@0.5.0: resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} dev: false @@ -10121,6 +10509,18 @@ packages: vfile: 5.3.7 dev: false + /unified@11.0.4: + resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + dependencies: + '@types/unist': 3.0.2 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.1.0 + vfile: 6.0.1 + dev: false + /unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'}