From a0a33087f66ddc89748902c3998dae928f0e7b70 Mon Sep 17 00:00:00 2001 From: tonyrewin Date: Sat, 8 Oct 2022 08:24:09 +0300 Subject: [PATCH] editor-ctrl-fixes --- package.json | 36 +- src/components/Editor/Sidebar.tsx | 59 ++-- src/components/Editor/prosemirror/context.ts | 10 +- src/components/Editor/prosemirror/ctrl.ts | 334 ++++++------------- src/components/Editor/prosemirror/p2p.ts | 6 +- src/components/Editor/prosemirror/setup.ts | 3 +- yarn.lock | 91 +---- 7 files changed, 153 insertions(+), 386 deletions(-) diff --git a/package.json b/package.json index 9077d4bf..f392ff43 100644 --- a/package.json +++ b/package.json @@ -29,24 +29,9 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.186.0", - "@nanostores/persistent": "^0.7.0", - "@nanostores/router": "^0.7.0", - "@nanostores/solid": "^0.3.0", - "@solid-primitives/memo": "^1.0.2", - "loglevel": "^1.8.0", - "loglevel-plugin-prefix": "^0.8.4", - "mailgun.js": "^8.0.1", - "markdown-it": "^13.0.1", - "markdown-it-container": "^3.0.0", - "markdown-it-implicit-figures": "^0.10.0", - "markdown-it-mark": "^3.0.1", - "markdown-it-replace-link": "^1.1.0", - "nanostores": "^0.7.0", - "postcss-modules": "^5.0.0" + "mailgun.js": "^8.0.1" }, "devDependencies": { - "@astrojs/language-server": "^0.27.0", - "@astrojs/markdown-remark": "^1.1.3", "@astrojs/solid-js": "^1.1.0", "@astrojs/vercel": "^2.1.0", "@babel/core": "^7.18.13", @@ -57,9 +42,13 @@ "@graphql-codegen/urql-introspection": "^2.2.1", "@graphql-tools/url-loader": "^7.16.4", "@graphql-typed-document-node/core": "^3.1.1", + "@nanostores/persistent": "^0.7.0", + "@nanostores/router": "^0.7.0", + "@nanostores/solid": "^0.3.0", "@popperjs/core": "^2.11.6", "@solid-devtools/debugger": "^0.11.1", "@solid-devtools/logger": "^0.4.9", + "@solid-primitives/memo": "^1.0.2", "@types/express": "^4.17.14", "@types/node": "^18.7.19", "@types/uuid": "^8.3.4", @@ -69,7 +58,7 @@ "@urql/devtools": "^2.0.3", "@urql/exchange-auth": "^1.0.0", "@urql/exchange-graphcache": "^5.0.0", - "astro": "^1.1.1", + "astro": "^1.4.6", "astro-eslint-parser": "^0.6.1", "bcryptjs": "^2.4.3", "bootstrap": "5.1.3", @@ -94,7 +83,16 @@ "idb": "^7.0.1", "jest": "^29.0.1", "lint-staged": "^13.0.3", + "loglevel": "^1.8.0", + "loglevel-plugin-prefix": "^0.8.4", + "markdown-it": "^13.0.1", + "markdown-it-container": "^3.0.0", + "markdown-it-implicit-figures": "^0.10.0", + "markdown-it-mark": "^3.0.1", + "markdown-it-replace-link": "^1.1.0", + "nanostores": "^0.7.0", "postcss": "^8.4.16", + "postcss-modules": "^5.0.0", "prettier": "^2.7.1", "prettier-eslint": "^15.0.1", "prosemirror-commands": "^1.3.1", @@ -111,10 +109,6 @@ "prosemirror-schema-list": "^1.2.2", "prosemirror-state": "^1.4.1", "prosemirror-view": "^1.28.1", - "rehype-autolink-headings": "^6.1.1", - "rehype-slug": "^5.0.1", - "rehype-toc": "^3.0.2", - "remark-code-titles": "^0.1.2", "rollup": "~2.79.1", "rollup-plugin-visualizer": "^5.8.2", "sass": "^1.55.0", diff --git a/src/components/Editor/Sidebar.tsx b/src/components/Editor/Sidebar.tsx index 08a8dac0..129c4923 100644 --- a/src/components/Editor/Sidebar.tsx +++ b/src/components/Editor/Sidebar.tsx @@ -1,9 +1,11 @@ -import { For, Show, createEffect, createSignal, onCleanup } from 'solid-js' +import { Show, createEffect, createSignal, onCleanup } from 'solid-js' import { unwrap } from 'solid-js/store' // import { undo, redo } from 'prosemirror-history' -import { File, useState /*, Config, PrettierConfig */ } from './prosemirror/context' +import { Draft, useState } from './prosemirror/context' import { clsx } from 'clsx' import type { Styled } from './Layout' +import { t } from '../../utils/intl' + // import type { EditorState } from 'prosemirror-state' // import { serialize } from './prosemirror/markdown' // import { baseUrl } from '../../graphql/client' @@ -11,12 +13,10 @@ import type { Styled } from './Layout' // const copy = async (text: string): Promise => navigator.clipboard.writeText(text) // const copyAllAsMarkdown = async (state: EditorState): Promise => -// !isServer && navigator.clipboard.writeText(serialize(state)) +// navigator.clipboard.writeText(serialize(state)) && !isServer const Off = (props: any) => - const Label = (props: Styled) => - const Link = ( props: Styled & { withMargin?: boolean; disabled?: boolean; title?: string; className?: string } ) => ( @@ -32,12 +32,12 @@ const Link = ( ) -type FileLinkProps = { - file: File - onOpenFile: (file: File) => void +type DraftLinkProps = { + draft: Draft + onOpenDraft: (draft: Draft) => void } -const FileLink = (props: FileLinkProps) => { +const DraftLink = (props: DraftLinkProps) => { const length = 100 let content = '' const getContent = (node: any) => { @@ -65,14 +65,14 @@ const FileLink = (props: FileLinkProps) => { } const text = () => - props.file.path - ? props.file.path.slice(Math.max(0, props.file.path.length - length)) - : getContent(props.file.text?.doc) + props.draft.path + ? props.draft.path.slice(Math.max(0, props.draft.path.length - length)) + : getContent(props.draft.text?.doc) return ( // eslint-disable-next-line solid/no-react-specific-props - props.onOpenFile(props.file)} data-testid="open"> - {text()} {props.file.path && '📎'} + props.onOpenDraft(props.draft)} data-testid="open"> + {text()} {props.draft.path && '📎'} ) } @@ -88,14 +88,13 @@ export const Sidebar = () => { // const collabText = () => (store.collab?.started ? 'Stop' : store.collab?.error ? 'Restart 🚨' : 'Start') const editorView = () => unwrap(store.editorView) // const onToggleMarkdown = () => ctrl.toggleMarkdown() - const onOpenFile = (file: File) => ctrl.openFile(unwrap(file)) + const onOpenDraft = (draft: Draft) => ctrl.openDraft(unwrap(draft)) // const collabUsers = () => store.collab?.y?.provider.awareness.meta.size ?? 0 // const onUndo = () => undo(editorView().state, editorView().dispatch) // const onRedo = () => redo(editorView().state, editorView().dispatch) // const onCopyAllAsMd = () => copyAllAsMarkdown(editorView().state).then(() => setLastAction('copy-md')) // const onToggleAlwaysOnTop = () => ctrl.updateConfig({ alwaysOnTop: !store.config.alwaysOnTop }) - // const onToggleFullscreen = () => ctrl.setFullscreen(!store.fullscreen) - // const onNew = () => ctrl.newFile() + // const onNew = () => ctrl.newDraft() // const onDiscard = () => ctrl.discard() const [isHidden, setIsHidden] = createSignal() @@ -106,7 +105,7 @@ export const Sidebar = () => { toggleSidebar() // const onSaveAs = async () => { - // const path = 'test' // TODO: save filename await remote.save(editorView().state) + // const path = 'test' // TODO: save draftname await remote.save(editorView().state) // // if (path) ctrl.updatePath(path) // } @@ -189,27 +188,17 @@ export const Sidebar = () => { {/* - - - Save to file - - New - {store.path ? 'Close' : store.files?.length > 0 && isEmpty(store.text) ? 'Delete ⚠️' : 'Clear'}{' '} + {store.path ? 'Close' : store.drafts?.length > 0 && isEmpty(store.text) ? 'Delete ⚠️' : 'Clear'}{' '} - - - Fullscreen {store.fullscreen && '✅'} - - Undo @@ -226,15 +215,15 @@ export const Sidebar = () => { Markdown mode {store.markdown && '✅'} Copy all as MD {lastAction() === 'copy-md' && '📋'} -*/} - 0}> -

Files:

+ + 0}> +

t('Drafts'):

- {(file) => } + {(draft) => }

- {/* + Collab {collabText()} diff --git a/src/components/Editor/prosemirror/context.ts b/src/components/Editor/prosemirror/context.ts index 4e4cc8ef..be941e44 100644 --- a/src/components/Editor/prosemirror/context.ts +++ b/src/components/Editor/prosemirror/context.ts @@ -55,10 +55,11 @@ export interface Collab { export type LoadingType = 'loading' | 'initialized' -export interface File { +// TODO: use this interface in prosemirror's context +export interface Draft { + path?: string // used by state text?: { [key: string]: string } lastModified?: string - path?: string markdown?: boolean } @@ -68,11 +69,9 @@ export interface State { extensions?: ProseMirrorExtension[] markdown?: boolean lastModified?: Date - files: File[] config: Config error?: ErrorObject loading: LoadingType - fullscreen: boolean collab?: Collab path?: string args?: Args @@ -103,15 +102,14 @@ const DEFAULT_CONFIG = { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const StateContext = createContext<[Store, any]>([{} as Store, undefined]) export const useState = () => useContext(StateContext) export const newState = (props: Partial = {}): State => ({ extensions: [], - files: [], loading: 'loading', - fullscreen: false, markdown: false, config: DEFAULT_CONFIG, ...props diff --git a/src/components/Editor/prosemirror/ctrl.ts b/src/components/Editor/prosemirror/ctrl.ts index 5134602c..70836e64 100644 --- a/src/components/Editor/prosemirror/ctrl.ts +++ b/src/components/Editor/prosemirror/ctrl.ts @@ -1,119 +1,116 @@ import { Store, createStore, unwrap } from 'solid-js/store' import { v4 as uuidv4 } from 'uuid' -import type { EditorState } from 'prosemirror-state' +import type { Command, EditorState } from 'prosemirror-state' import { undo, redo } from 'prosemirror-history' import { selectAll, deleteSelection } from 'prosemirror-commands' import { undo as yUndo, redo as yRedo } from 'y-prosemirror' -import { debounce } from 'lodash' +import debounce from 'lodash/debounce' import { createSchema, createExtensions, createEmptyText, InitOpts } from '../prosemirror/setup' -import { State, File, Config, ServiceError, newState, PeerData } from '../prosemirror/context' +import { State, Config, ServiceError, newState, PeerData } from '../prosemirror/context' import { serialize, createMarkdownParser } from '../prosemirror/markdown' import { isEmpty, isInitialized, ProseMirrorExtension } from '../prosemirror/state' import { isServer } from 'solid-js/web' import { roomConnect } from '../prosemirror/p2p' const mod = 'Ctrl' -const isText = (x): boolean => x && x.doc && x.selection -const isState = (x): boolean => typeof x.lastModified !== 'string' && Array.isArray(x.files) -const isFile = (x): boolean => x && (x.text || x.path) +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const createCtrl = (initial: State): [Store, { [key: string]: any }] => { const [store, setState] = createStore(initial) const discardText = async () => { const state = unwrap(store) - const index = state.files.length - 1 - const file = index !== -1 ? state.files[index] : undefined - let next: Partial - - if (file) { - next = await createTextFromFile(file) - } else { - const extensions = createExtensions({ - config: state.config ?? store.config, - markdown: state.markdown && store.markdown, - keymap - }) - - next = { - text: createEmptyText(), - extensions, - lastModified: undefined, - path: undefined, - markdown: state.markdown - } - } - - const files = state.files.filter((f: File) => f !== file) + const extensions = createExtensions({ + config: state.config ?? store.config, + markdown: state.markdown && store.markdown, + keymap + }) setState({ - files, - ...next, - collab: file ? undefined : state.collab, + text: createEmptyText(), + extensions, + lastModified: undefined, + path: undefined, + markdown: state.markdown, + collab: state.collab, error: undefined }) } - const addToFiles = (files: File[], prev: State) => { - const text = prev.path ? undefined : (prev.text as EditorState).toJSON() - - return [ - ...files, - { - text, - lastModified: prev.lastModified?.toISOString(), - path: prev.path, - markdown: prev.markdown - } - ] - } - const discard = async () => { if (store.path) { await discardText() - } else if (store.files?.length > 0 && isEmpty(store.text)) { - await discardText() } else { selectAll(store.editorView.state, store.editorView.dispatch) deleteSelection(store.editorView.state, store.editorView.dispatch) } + return true } const onDiscard = () => { discard() - return true } - const onToggleMarkdown = () => toggleMarkdown() - const onUndo = () => { - if (!isInitialized(store.text)) return - + if (!isInitialized(store.text)) return false const text = store.text as EditorState - - if (store.collab?.started) { - yUndo(text) - } else { - undo(text, store.editorView.dispatch) - } - + if (store.collab?.started) yUndo(text) + else undo(text, store.editorView.dispatch) return true } const onRedo = () => { - if (!isInitialized(store.text)) return - + if (!isInitialized(store.text)) return false const text = store.text as EditorState + if (store.collab?.started) yRedo(text) + else redo(text, store.editorView.dispatch) + return true + } - if (store.collab?.started) { - yRedo(text) + const toggleMarkdown = () => { + const state = unwrap(store) + const editorState = store.text as EditorState + const markdown = !state.markdown + const selection = { type: 'text', anchor: 1, head: 1 } + let doc + if (markdown) { + const lines = serialize(editorState).split('\n') + const nodes = lines.map((text) => { + return text ? { type: 'paragraph', content: [{ type: 'text', text }] } : { type: 'paragraph' } + }) + doc = { type: 'doc', content: nodes } } else { - redo(text, store.editorView.dispatch) + const schema = createSchema({ + config: state.config, + path: state.path, + y: state.collab?.y, + markdown, + keymap + }) + const parser = createMarkdownParser(schema) + let textContent = '' + editorState.doc.forEach((node) => { + textContent += `${node.textContent}\n` + }) + const text = parser.parse(textContent) + doc = text?.toJSON() } - return true + const extensions = createExtensions({ + config: state.config, + markdown, + path: state.path, + keymap, + y: state.collab?.y + }) + + setState({ + text: { selection, doc }, + extensions, + markdown + }) } const keymap = { @@ -121,85 +118,52 @@ export const createCtrl = (initial: State): [Store, { [key: string]: any [`${mod}-z`]: onUndo, [`Shift-${mod}-z`]: onRedo, [`${mod}-y`]: onRedo, - [`${mod}-m`]: onToggleMarkdown - } - - const createTextFromFile = async (file: File) => { - const state = unwrap(store) - - // if (file.path) file = await loadFile(state.config, file.path) - - const extensions = createExtensions({ - config: state.config, - markdown: file.markdown, - path: file.path, - keymap - }) - - return { - text: file.text, - extensions, - lastModified: file.lastModified ? new Date(file.lastModified) : undefined, - path: file.path, - markdown: file.markdown - } - } + [`${mod}-m`]: toggleMarkdown + } as unknown as { [key: string]: Command } const fetchData = async (): Promise => { - const state: State = unwrap(store) - const room = window.location.pathname?.slice(1).trim() - const args = { room: room || undefined } if (isServer) return + const state: State = unwrap(store) + const room = undefined // window.location.pathname?.slice(1) + uuidv4() + // console.debug('[editor-ctrl] got unique room', room) + const args = { room } const { default: db } = await import('../db') const data: string = await db.get('state') + console.debug('[editor-ctrl] got stored state from idb') let parsed + let text = state.text if (data !== undefined) { try { parsed = JSON.parse(data) if (!parsed) return { ...state, args } + + console.debug('[editor-ctrl] json state parsed successfully', parsed) + if (parsed?.text) { + if (!parsed.text || !parsed.text.doc || !parsed.text.selection) { + throw new ServiceError('invalid_state', parsed.text) + } else { + text = parsed.text + console.debug('[editor-ctrl] got text from stored json', parsed) + } + } + return { + ...parsed, + text, + extensions: createExtensions({ + path: parsed.path, + markdown: parsed.markdown, + keymap, + config: {} as Config + }), + args, + lastModified: parsed.lastModified ? new Date(parsed.lastModified) : new Date() + } } catch { throw new ServiceError('invalid_state', data) } } - let text = state.text - if (parsed.text) { - if (!isText(parsed.text)) { - throw new ServiceError('invalid_state', parsed.text) - } - text = parsed.text - } - - const extensions = createExtensions({ - path: parsed.path, - markdown: parsed.markdown, - keymap, - config: {} as Config - }) - - const nState = { - ...parsed, - text, - extensions, - // config, - args - } - - if (nState.lastModified) { - nState.lastModified = new Date(nState.lastModified) - } - - for (const file of parsed.files) { - if (!isFile(file)) { - throw new ServiceError('invalid_file', file) - } - } - if (!isState(nState)) { - throw new ServiceError('invalid_state', nState) - } - - return nState } const getTheme = (state: State) => ({ theme: state.config.theme }) @@ -208,8 +172,6 @@ export const createCtrl = (initial: State): [Store, { [key: string]: any setState({ ...newState(), loading: 'initialized', - files: [], - fullscreen: store.fullscreen, lastModified: new Date(), error: undefined, text: undefined @@ -217,67 +179,34 @@ export const createCtrl = (initial: State): [Store, { [key: string]: any } const init = async () => { - let data = await fetchData() + let state = await fetchData() + console.debug('[editor-ctrl] state initiated', state) try { - if (data.args?.room) { - data = doStartCollab(data) - } else if (data.args?.text) { - data = await doOpenFile(data, { text: JSON.parse(data.args?.text) }) - } else if (!data.text) { + if (state.args?.room) { + state = doStartCollab(state) + } else if (!state.text) { const text = createEmptyText() const extensions = createExtensions({ - config: data.config, - markdown: data.markdown, + config: state.config, + markdown: state.markdown, keymap }) - data = { ...data, text, extensions } + state = { ...state, text, extensions } } } catch (error) { - data = { ...data, error } + state = { ...state, error } } setState({ - ...data, - config: { ...data.config, ...getTheme(data) }, + ...state, + config: { ...state.config, ...getTheme(state) }, loading: 'initialized' }) } - const doOpenFile = async (state: State, file: File): Promise => { - const findIndexOfFile = (f: File) => { - for (let i = 0; i < state.files.length; i++) { - if (state.files[i] === f) return i - - if (f.path && state.files[i].path === f.path) return i - } - - return -1 - } - - const index = findIndexOfFile(file) - const item = index === -1 ? file : state.files[index] - let files = state.files.filter((f) => f !== item) - - if (!isEmpty(state.text) && state.lastModified) { - files = addToFiles(files, state) - } - - file.lastModified = item.lastModified - const next = await createTextFromFile(file) - - return { - ...state, - ...next, - files, - collab: undefined, - error: undefined - } - } - const saveState = debounce(async (state: State) => { const data = { lastModified: state.lastModified, - files: state.files, config: state.config, path: state.path, markdown: state.markdown, @@ -322,15 +251,8 @@ export const createCtrl = (initial: State): [Store, { [key: string]: any let nState = state if ((backup && !isEmpty(state.text)) || state.path) { - let files = state.files - - if (!state.error) { - files = addToFiles(files, state) - } - nState = { ...state, - files, lastModified: undefined, path: undefined, error: undefined @@ -357,54 +279,6 @@ export const createCtrl = (initial: State): [Store, { [key: string]: any window.history.replaceState(null, '', '/') } - const toggleMarkdown = () => { - const state = unwrap(store) - const editorState = store.text as EditorState - const markdown = !state.markdown - const selection = { type: 'text', anchor: 1, head: 1 } - let doc - - if (markdown) { - const lines = serialize(editorState).split('\n') - const nodes = lines.map((text) => { - return text ? { type: 'paragraph', content: [{ type: 'text', text }] } : { type: 'paragraph' } - }) - - doc = { type: 'doc', content: nodes } - } else { - const schema = createSchema({ - config: state.config, - path: state.path, - y: state.collab?.y, - markdown, - keymap - }) - - const parser = createMarkdownParser(schema) - let textContent = '' - - editorState.doc.forEach((node) => { - textContent += `${node.textContent}\n` - }) - const text = parser.parse(textContent) - doc = text?.toJSON() - } - - const extensions = createExtensions({ - config: state.config, - markdown, - path: state.path, - keymap, - y: state.collab?.y - }) - - setState({ - text: { selection, doc }, - extensions, - markdown - }) - } - const updateConfig = (config: Partial) => { const state = unwrap(store) const extensions = createExtensions({ diff --git a/src/components/Editor/prosemirror/p2p.ts b/src/components/Editor/prosemirror/p2p.ts index dce80ea5..0349315a 100644 --- a/src/components/Editor/prosemirror/p2p.ts +++ b/src/components/Editor/prosemirror/p2p.ts @@ -1,12 +1,12 @@ import { uniqueNamesGenerator, adjectives, animals } from 'unique-names-generator' import { Awareness } from 'y-protocols/awareness' import { WebrtcProvider } from 'y-webrtc' -import * as Y from 'yjs' +import { Doc, XmlFragment } from 'yjs' import type { Reaction } from '../../../graphql/types.gen' import { setReactions } from '../../../stores/editor' -export const roomConnect = (room, username = '', keyname = 'collab'): [Y.XmlFragment, WebrtcProvider] => { - const ydoc = new Y.Doc() +export const roomConnect = (room, username = '', keyname = 'collab'): [XmlFragment, WebrtcProvider] => { + const ydoc = new Doc() const yarr = ydoc.getArray(keyname + '-reactions') const yXmlFragment = ydoc.getXmlFragment(keyname) const webrtcOptions = { diff --git a/src/components/Editor/prosemirror/setup.ts b/src/components/Editor/prosemirror/setup.ts index 1493c591..3502a512 100644 --- a/src/components/Editor/prosemirror/setup.ts +++ b/src/components/Editor/prosemirror/setup.ts @@ -17,10 +17,11 @@ import table from './extension/table' import collab from './extension/collab' import type { Config, PeerData } from './context' import selectionMenu from './extension/selection' +import type { Command } from 'prosemirror-state' export interface InitOpts { data?: unknown - keymap?: any + keymap?: { [key: string]: Command } config: Config markdown: boolean path?: string diff --git a/yarn.lock b/yarn.lock index ab87d8c5..a8adb5b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -73,24 +73,6 @@ vscode-languageserver-types "^3.17.1" vscode-uri "^3.0.3" -"@astrojs/language-server@^0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@astrojs/language-server/-/language-server-0.27.0.tgz#5182965a1158e77bfcd9211edf0f8a5fef3c2505" - integrity sha512-4nT2KqAhxjjElATs/4Q8nkiUlu+YalJqZIEW4YOGEoSDbju/pw7fy8CJHFOhkPmGux8173N58i6l1cewGcxluw== - dependencies: - "@vscode/emmet-helper" "^2.8.4" - events "^3.3.0" - prettier "^2.7.1" - prettier-plugin-astro "^0.5.3" - source-map "^0.7.3" - vscode-css-languageservice "^6.0.1" - vscode-html-languageservice "^5.0.0" - vscode-languageserver "^8.0.1" - vscode-languageserver-protocol "^3.17.1" - vscode-languageserver-textdocument "^1.0.4" - vscode-languageserver-types "^3.17.1" - vscode-uri "^3.0.3" - "@astrojs/markdown-remark@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@astrojs/markdown-remark/-/markdown-remark-1.1.3.tgz#9fa985a532622043f0863c20f01c6ed01eca31e2" @@ -2307,11 +2289,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jsdevtools/rehype-toc@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@jsdevtools/rehype-toc/-/rehype-toc-3.0.2.tgz#29c32e6b40cd4b5dafd96cb90d5057ac5dab4a51" - integrity sha512-n5JEf16Wr4mdkRMZ8wMP/wN9/sHmTjRPbouXjJH371mZ2LEGDl72t8tEsMRNFerQN/QJtivOxqK1frdGa4QK5Q== - "@ljharb/has-package-exports-patterns@^0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@ljharb/has-package-exports-patterns/-/has-package-exports-patterns-0.0.2.tgz#c1718939b65efa1f45f53686c2fcfa992b9fb68f" @@ -5722,7 +5699,7 @@ git-hooks-list@^3.0.0: resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.0.0.tgz#6d888988bb445b34e7c2e1eb97cb88358153221e" integrity sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ== -github-slugger@^1.1.1, github-slugger@^1.4.0: +github-slugger@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== @@ -6001,13 +5978,6 @@ hast-util-has-property@^2.0.0: resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz#c15cd6180f3e535540739fcc9787bcffb5708cae" integrity sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w== -hast-util-heading-rank@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hast-util-heading-rank/-/hast-util-heading-rank-2.1.0.tgz#c39f34fa8330ebfec03a08b5d5019ed56122029c" - integrity sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ== - dependencies: - "@types/hast" "^2.0.0" - hast-util-is-element@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz#fc0b0dc7cef3895e839b8d66979d57b0338c68f3" @@ -9312,19 +9282,6 @@ regexpp@^3.0.0, regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -rehype-autolink-headings@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz#0cb874a56f3de6ead1c2268d7f0fc5006f244db5" - integrity sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA== - dependencies: - "@types/hast" "^2.0.0" - extend "^3.0.0" - hast-util-has-property "^2.0.0" - hast-util-heading-rank "^2.0.0" - hast-util-is-element "^2.0.0" - unified "^10.0.0" - unist-util-visit "^4.0.0" - rehype-parse@^8.0.0: version "8.0.4" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688" @@ -9344,19 +9301,6 @@ rehype-raw@^6.1.1: hast-util-raw "^7.2.0" unified "^10.0.0" -rehype-slug@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-5.0.1.tgz#6e732d0c55b3b1e34187e74b7363fb53229e5f52" - integrity sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA== - dependencies: - "@types/hast" "^2.0.0" - github-slugger "^1.1.1" - hast-util-has-property "^2.0.0" - hast-util-heading-rank "^2.0.0" - hast-util-to-string "^2.0.0" - unified "^10.0.0" - unist-util-visit "^4.0.0" - rehype-stringify@^9.0.0, rehype-stringify@^9.0.3: version "9.0.3" resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-9.0.3.tgz#70e3bd6d4d29e7acf36b802deed350305d2c3c17" @@ -9366,13 +9310,6 @@ rehype-stringify@^9.0.0, rehype-stringify@^9.0.3: hast-util-to-html "^8.0.0" unified "^10.0.0" -rehype-toc@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rehype-toc/-/rehype-toc-3.0.2.tgz#0373e2abafddeb0606ee38229ff6714da6d86d68" - integrity sha512-DMt376+4i1KJGgHJL7Ezd65qKkJ7Eqp6JSB47BJ90ReBrohI9ufrornArM6f4oJjP2E2DVZZHufWucv/9t7GUQ== - dependencies: - "@jsdevtools/rehype-toc" "3.0.2" - rehype@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/rehype/-/rehype-12.0.1.tgz#68a317662576dcaa2565a3952e149d6900096bf6" @@ -9392,13 +9329,6 @@ relay-runtime@12.0.0: fbjs "^3.0.0" invariant "^2.2.4" -remark-code-titles@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/remark-code-titles/-/remark-code-titles-0.1.2.tgz#ae41b47c517eae4084c761a59a60df5f0bd54aa8" - integrity sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw== - dependencies: - unist-util-visit "^1.4.0" - remark-gfm@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" @@ -10739,11 +10669,6 @@ unist-util-generated@^2.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== -unist-util-is@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" - integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== - unist-util-is@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" @@ -10797,13 +10722,6 @@ unist-util-visit-children@^1.0.0: resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-1.1.4.tgz#e8a087e58a33a2815f76ea1901c15dec2cb4b432" integrity sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ== -unist-util-visit-parents@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" - integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== - dependencies: - unist-util-is "^3.0.0" - unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz#868f353e6fce6bf8fa875b251b0f4fec3be709bb" @@ -10812,13 +10730,6 @@ unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" -unist-util-visit@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" - integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== - dependencies: - unist-util-visit-parents "^2.0.0" - unist-util-visit@^4.0.0, unist-util-visit@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.1.tgz#1c4842d70bd3df6cc545276f5164f933390a9aad"