editor-ctrl-fixes
This commit is contained in:
parent
adc0fe6393
commit
a0a33087f6
36
package.json
36
package.json
|
@ -29,24 +29,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.186.0",
|
"@aws-sdk/client-s3": "^3.186.0",
|
||||||
"@nanostores/persistent": "^0.7.0",
|
"mailgun.js": "^8.0.1"
|
||||||
"@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"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/language-server": "^0.27.0",
|
|
||||||
"@astrojs/markdown-remark": "^1.1.3",
|
|
||||||
"@astrojs/solid-js": "^1.1.0",
|
"@astrojs/solid-js": "^1.1.0",
|
||||||
"@astrojs/vercel": "^2.1.0",
|
"@astrojs/vercel": "^2.1.0",
|
||||||
"@babel/core": "^7.18.13",
|
"@babel/core": "^7.18.13",
|
||||||
|
@ -57,9 +42,13 @@
|
||||||
"@graphql-codegen/urql-introspection": "^2.2.1",
|
"@graphql-codegen/urql-introspection": "^2.2.1",
|
||||||
"@graphql-tools/url-loader": "^7.16.4",
|
"@graphql-tools/url-loader": "^7.16.4",
|
||||||
"@graphql-typed-document-node/core": "^3.1.1",
|
"@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",
|
"@popperjs/core": "^2.11.6",
|
||||||
"@solid-devtools/debugger": "^0.11.1",
|
"@solid-devtools/debugger": "^0.11.1",
|
||||||
"@solid-devtools/logger": "^0.4.9",
|
"@solid-devtools/logger": "^0.4.9",
|
||||||
|
"@solid-primitives/memo": "^1.0.2",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/node": "^18.7.19",
|
"@types/node": "^18.7.19",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
|
@ -69,7 +58,7 @@
|
||||||
"@urql/devtools": "^2.0.3",
|
"@urql/devtools": "^2.0.3",
|
||||||
"@urql/exchange-auth": "^1.0.0",
|
"@urql/exchange-auth": "^1.0.0",
|
||||||
"@urql/exchange-graphcache": "^5.0.0",
|
"@urql/exchange-graphcache": "^5.0.0",
|
||||||
"astro": "^1.1.1",
|
"astro": "^1.4.6",
|
||||||
"astro-eslint-parser": "^0.6.1",
|
"astro-eslint-parser": "^0.6.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"bootstrap": "5.1.3",
|
"bootstrap": "5.1.3",
|
||||||
|
@ -94,7 +83,16 @@
|
||||||
"idb": "^7.0.1",
|
"idb": "^7.0.1",
|
||||||
"jest": "^29.0.1",
|
"jest": "^29.0.1",
|
||||||
"lint-staged": "^13.0.3",
|
"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": "^8.4.16",
|
||||||
|
"postcss-modules": "^5.0.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-eslint": "^15.0.1",
|
"prettier-eslint": "^15.0.1",
|
||||||
"prosemirror-commands": "^1.3.1",
|
"prosemirror-commands": "^1.3.1",
|
||||||
|
@ -111,10 +109,6 @@
|
||||||
"prosemirror-schema-list": "^1.2.2",
|
"prosemirror-schema-list": "^1.2.2",
|
||||||
"prosemirror-state": "^1.4.1",
|
"prosemirror-state": "^1.4.1",
|
||||||
"prosemirror-view": "^1.28.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": "~2.79.1",
|
||||||
"rollup-plugin-visualizer": "^5.8.2",
|
"rollup-plugin-visualizer": "^5.8.2",
|
||||||
"sass": "^1.55.0",
|
"sass": "^1.55.0",
|
||||||
|
|
|
@ -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 { unwrap } from 'solid-js/store'
|
||||||
// import { undo, redo } from 'prosemirror-history'
|
// 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 { clsx } from 'clsx'
|
||||||
import type { Styled } from './Layout'
|
import type { Styled } from './Layout'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
// import type { EditorState } from 'prosemirror-state'
|
// import type { EditorState } from 'prosemirror-state'
|
||||||
// import { serialize } from './prosemirror/markdown'
|
// import { serialize } from './prosemirror/markdown'
|
||||||
// import { baseUrl } from '../../graphql/client'
|
// import { baseUrl } from '../../graphql/client'
|
||||||
|
@ -11,12 +13,10 @@ import type { Styled } from './Layout'
|
||||||
|
|
||||||
// const copy = async (text: string): Promise<void> => navigator.clipboard.writeText(text)
|
// const copy = async (text: string): Promise<void> => navigator.clipboard.writeText(text)
|
||||||
// const copyAllAsMarkdown = async (state: EditorState): Promise<void> =>
|
// const copyAllAsMarkdown = async (state: EditorState): Promise<void> =>
|
||||||
// !isServer && navigator.clipboard.writeText(serialize(state))
|
// navigator.clipboard.writeText(serialize(state)) && !isServer
|
||||||
|
|
||||||
const Off = (props: any) => <div class="sidebar-off">{props.children}</div>
|
const Off = (props: any) => <div class="sidebar-off">{props.children}</div>
|
||||||
|
|
||||||
const Label = (props: Styled) => <h3 class="sidebar-label">{props.children}</h3>
|
const Label = (props: Styled) => <h3 class="sidebar-label">{props.children}</h3>
|
||||||
|
|
||||||
const Link = (
|
const Link = (
|
||||||
props: Styled & { withMargin?: boolean; disabled?: boolean; title?: string; className?: string }
|
props: Styled & { withMargin?: boolean; disabled?: boolean; title?: string; className?: string }
|
||||||
) => (
|
) => (
|
||||||
|
@ -32,12 +32,12 @@ const Link = (
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileLinkProps = {
|
type DraftLinkProps = {
|
||||||
file: File
|
draft: Draft
|
||||||
onOpenFile: (file: File) => void
|
onOpenDraft: (draft: Draft) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileLink = (props: FileLinkProps) => {
|
const DraftLink = (props: DraftLinkProps) => {
|
||||||
const length = 100
|
const length = 100
|
||||||
let content = ''
|
let content = ''
|
||||||
const getContent = (node: any) => {
|
const getContent = (node: any) => {
|
||||||
|
@ -65,14 +65,14 @@ const FileLink = (props: FileLinkProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = () =>
|
const text = () =>
|
||||||
props.file.path
|
props.draft.path
|
||||||
? props.file.path.slice(Math.max(0, props.file.path.length - length))
|
? props.draft.path.slice(Math.max(0, props.draft.path.length - length))
|
||||||
: getContent(props.file.text?.doc)
|
: getContent(props.draft.text?.doc)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line solid/no-react-specific-props
|
// eslint-disable-next-line solid/no-react-specific-props
|
||||||
<Link className="file" onClick={() => props.onOpenFile(props.file)} data-testid="open">
|
<Link className="draft" onClick={() => props.onOpenDraft(props.draft)} data-testid="open">
|
||||||
{text()} {props.file.path && '📎'}
|
{text()} {props.draft.path && '📎'}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -88,14 +88,13 @@ export const Sidebar = () => {
|
||||||
// const collabText = () => (store.collab?.started ? 'Stop' : store.collab?.error ? 'Restart 🚨' : 'Start')
|
// const collabText = () => (store.collab?.started ? 'Stop' : store.collab?.error ? 'Restart 🚨' : 'Start')
|
||||||
const editorView = () => unwrap(store.editorView)
|
const editorView = () => unwrap(store.editorView)
|
||||||
// const onToggleMarkdown = () => ctrl.toggleMarkdown()
|
// 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 collabUsers = () => store.collab?.y?.provider.awareness.meta.size ?? 0
|
||||||
// const onUndo = () => undo(editorView().state, editorView().dispatch)
|
// const onUndo = () => undo(editorView().state, editorView().dispatch)
|
||||||
// const onRedo = () => redo(editorView().state, editorView().dispatch)
|
// const onRedo = () => redo(editorView().state, editorView().dispatch)
|
||||||
// const onCopyAllAsMd = () => copyAllAsMarkdown(editorView().state).then(() => setLastAction('copy-md'))
|
// const onCopyAllAsMd = () => copyAllAsMarkdown(editorView().state).then(() => setLastAction('copy-md'))
|
||||||
// const onToggleAlwaysOnTop = () => ctrl.updateConfig({ alwaysOnTop: !store.config.alwaysOnTop })
|
// const onToggleAlwaysOnTop = () => ctrl.updateConfig({ alwaysOnTop: !store.config.alwaysOnTop })
|
||||||
// const onToggleFullscreen = () => ctrl.setFullscreen(!store.fullscreen)
|
// const onNew = () => ctrl.newDraft()
|
||||||
// const onNew = () => ctrl.newFile()
|
|
||||||
// const onDiscard = () => ctrl.discard()
|
// const onDiscard = () => ctrl.discard()
|
||||||
const [isHidden, setIsHidden] = createSignal<boolean | false>()
|
const [isHidden, setIsHidden] = createSignal<boolean | false>()
|
||||||
|
|
||||||
|
@ -106,7 +105,7 @@ export const Sidebar = () => {
|
||||||
toggleSidebar()
|
toggleSidebar()
|
||||||
|
|
||||||
// const onSaveAs = async () => {
|
// 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)
|
// if (path) ctrl.updatePath(path)
|
||||||
// }
|
// }
|
||||||
|
@ -189,27 +188,17 @@ export const Sidebar = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
<Show when={isTauri && !store.path}>
|
|
||||||
<Link onClick={onSaveAs}>
|
|
||||||
Save to file <Keys keys={[mod, 's']} />
|
|
||||||
</Link>
|
|
||||||
</Show>
|
|
||||||
<Link onClick={onNew} data-testid='new'>
|
<Link onClick={onNew} data-testid='new'>
|
||||||
New <Keys keys={[mod, 'n']} />
|
New <Keys keys={[mod, 'n']} />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
onClick={onDiscard}
|
onClick={onDiscard}
|
||||||
disabled={!store.path && store.files?.length === 0 && isEmpty(store.text)}
|
disabled={!store.path && store.drafts?.length === 0 && isEmpty(store.text)}
|
||||||
data-testid='discard'
|
data-testid='discard'
|
||||||
>
|
>
|
||||||
{store.path ? 'Close' : store.files?.length > 0 && isEmpty(store.text) ? 'Delete ⚠️' : 'Clear'}{' '}
|
{store.path ? 'Close' : store.drafts?.length > 0 && isEmpty(store.text) ? 'Delete ⚠️' : 'Clear'}{' '}
|
||||||
<Keys keys={[mod, 'w']} />
|
<Keys keys={[mod, 'w']} />
|
||||||
</Link>
|
</Link>
|
||||||
<Show when={isTauri}>
|
|
||||||
<Link onClick={onToggleFullscreen}>
|
|
||||||
Fullscreen {store.fullscreen && '✅'} <Keys keys={[alt, 'Enter']} />
|
|
||||||
</Link>
|
|
||||||
</Show>
|
|
||||||
<Link onClick={onUndo}>
|
<Link onClick={onUndo}>
|
||||||
Undo <Keys keys={[mod, 'z']} />
|
Undo <Keys keys={[mod, 'z']} />
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -226,15 +215,15 @@ export const Sidebar = () => {
|
||||||
Markdown mode {store.markdown && '✅'} <Keys keys={[mod, 'm']} />
|
Markdown mode {store.markdown && '✅'} <Keys keys={[mod, 'm']} />
|
||||||
</Link>
|
</Link>
|
||||||
<Link onClick={onCopyAllAsMd}>Copy all as MD {lastAction() === 'copy-md' && '📋'}</Link>
|
<Link onClick={onCopyAllAsMd}>Copy all as MD {lastAction() === 'copy-md' && '📋'}</Link>
|
||||||
*/}
|
|
||||||
<Show when={store.files?.length > 0}>
|
<Show when={store.drafts?.length > 0}>
|
||||||
<h4>Files:</h4>
|
<h4>t('Drafts'):</h4>
|
||||||
<p>
|
<p>
|
||||||
<For each={store.files}>{(file) => <FileLink file={file} onOpenFile={onOpenFile} />}</For>
|
<For each={store.drafts}>{(draft) => <DraftLink draft={draft} onOpenDraft={onOpenDraft} />}</For>
|
||||||
</p>
|
</p>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/*
|
|
||||||
<Link onClick={onCollab} title={store.collab?.error ? 'Connection error' : ''}>
|
<Link onClick={onCollab} title={store.collab?.error ? 'Connection error' : ''}>
|
||||||
Collab {collabText()}
|
Collab {collabText()}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -55,10 +55,11 @@ export interface Collab {
|
||||||
|
|
||||||
export type LoadingType = 'loading' | 'initialized'
|
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 }
|
text?: { [key: string]: string }
|
||||||
lastModified?: string
|
lastModified?: string
|
||||||
path?: string
|
|
||||||
markdown?: boolean
|
markdown?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,11 +69,9 @@ export interface State {
|
||||||
extensions?: ProseMirrorExtension[]
|
extensions?: ProseMirrorExtension[]
|
||||||
markdown?: boolean
|
markdown?: boolean
|
||||||
lastModified?: Date
|
lastModified?: Date
|
||||||
files: File[]
|
|
||||||
config: Config
|
config: Config
|
||||||
error?: ErrorObject
|
error?: ErrorObject
|
||||||
loading: LoadingType
|
loading: LoadingType
|
||||||
fullscreen: boolean
|
|
||||||
collab?: Collab
|
collab?: Collab
|
||||||
path?: string
|
path?: string
|
||||||
args?: Args
|
args?: Args
|
||||||
|
@ -103,15 +102,14 @@ const DEFAULT_CONFIG = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const StateContext = createContext<[Store<State>, any]>([{} as Store<State>, undefined])
|
export const StateContext = createContext<[Store<State>, any]>([{} as Store<State>, undefined])
|
||||||
|
|
||||||
export const useState = () => useContext(StateContext)
|
export const useState = () => useContext(StateContext)
|
||||||
|
|
||||||
export const newState = (props: Partial<State> = {}): State => ({
|
export const newState = (props: Partial<State> = {}): State => ({
|
||||||
extensions: [],
|
extensions: [],
|
||||||
files: [],
|
|
||||||
loading: 'loading',
|
loading: 'loading',
|
||||||
fullscreen: false,
|
|
||||||
markdown: false,
|
markdown: false,
|
||||||
config: DEFAULT_CONFIG,
|
config: DEFAULT_CONFIG,
|
||||||
...props
|
...props
|
||||||
|
|
|
@ -1,119 +1,116 @@
|
||||||
import { Store, createStore, unwrap } from 'solid-js/store'
|
import { Store, createStore, unwrap } from 'solid-js/store'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
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 { undo, redo } from 'prosemirror-history'
|
||||||
import { selectAll, deleteSelection } from 'prosemirror-commands'
|
import { selectAll, deleteSelection } from 'prosemirror-commands'
|
||||||
import { undo as yUndo, redo as yRedo } from 'y-prosemirror'
|
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 { 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 { serialize, createMarkdownParser } from '../prosemirror/markdown'
|
||||||
import { isEmpty, isInitialized, ProseMirrorExtension } from '../prosemirror/state'
|
import { isEmpty, isInitialized, ProseMirrorExtension } from '../prosemirror/state'
|
||||||
import { isServer } from 'solid-js/web'
|
import { isServer } from 'solid-js/web'
|
||||||
import { roomConnect } from '../prosemirror/p2p'
|
import { roomConnect } from '../prosemirror/p2p'
|
||||||
|
|
||||||
const mod = 'Ctrl'
|
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<State>, { [key: string]: any }] => {
|
export const createCtrl = (initial: State): [Store<State>, { [key: string]: any }] => {
|
||||||
const [store, setState] = createStore(initial)
|
const [store, setState] = createStore(initial)
|
||||||
|
|
||||||
const discardText = async () => {
|
const discardText = async () => {
|
||||||
const state = unwrap(store)
|
const state = unwrap(store)
|
||||||
const index = state.files.length - 1
|
|
||||||
const file = index !== -1 ? state.files[index] : undefined
|
|
||||||
|
|
||||||
let next: Partial<State>
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
next = await createTextFromFile(file)
|
|
||||||
} else {
|
|
||||||
const extensions = createExtensions({
|
const extensions = createExtensions({
|
||||||
config: state.config ?? store.config,
|
config: state.config ?? store.config,
|
||||||
markdown: state.markdown && store.markdown,
|
markdown: state.markdown && store.markdown,
|
||||||
keymap
|
keymap
|
||||||
})
|
})
|
||||||
|
|
||||||
next = {
|
setState({
|
||||||
text: createEmptyText(),
|
text: createEmptyText(),
|
||||||
extensions,
|
extensions,
|
||||||
lastModified: undefined,
|
lastModified: undefined,
|
||||||
path: undefined,
|
path: undefined,
|
||||||
markdown: state.markdown
|
markdown: state.markdown,
|
||||||
}
|
collab: state.collab,
|
||||||
}
|
|
||||||
|
|
||||||
const files = state.files.filter((f: File) => f !== file)
|
|
||||||
|
|
||||||
setState({
|
|
||||||
files,
|
|
||||||
...next,
|
|
||||||
collab: file ? undefined : state.collab,
|
|
||||||
error: undefined
|
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 () => {
|
const discard = async () => {
|
||||||
if (store.path) {
|
if (store.path) {
|
||||||
await discardText()
|
await discardText()
|
||||||
} else if (store.files?.length > 0 && isEmpty(store.text)) {
|
|
||||||
await discardText()
|
|
||||||
} else {
|
} else {
|
||||||
selectAll(store.editorView.state, store.editorView.dispatch)
|
selectAll(store.editorView.state, store.editorView.dispatch)
|
||||||
deleteSelection(store.editorView.state, store.editorView.dispatch)
|
deleteSelection(store.editorView.state, store.editorView.dispatch)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDiscard = () => {
|
const onDiscard = () => {
|
||||||
discard()
|
discard()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onToggleMarkdown = () => toggleMarkdown()
|
|
||||||
|
|
||||||
const onUndo = () => {
|
const onUndo = () => {
|
||||||
if (!isInitialized(store.text)) return
|
if (!isInitialized(store.text)) return false
|
||||||
|
|
||||||
const text = store.text as EditorState
|
const text = store.text as EditorState
|
||||||
|
if (store.collab?.started) yUndo(text)
|
||||||
if (store.collab?.started) {
|
else undo(text, store.editorView.dispatch)
|
||||||
yUndo(text)
|
|
||||||
} else {
|
|
||||||
undo(text, store.editorView.dispatch)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRedo = () => {
|
const onRedo = () => {
|
||||||
if (!isInitialized(store.text)) return
|
if (!isInitialized(store.text)) return false
|
||||||
|
|
||||||
const text = store.text as EditorState
|
const text = store.text as EditorState
|
||||||
|
if (store.collab?.started) yRedo(text)
|
||||||
if (store.collab?.started) {
|
else redo(text, store.editorView.dispatch)
|
||||||
yRedo(text)
|
return true
|
||||||
} else {
|
|
||||||
redo(text, store.editorView.dispatch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
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 keymap = {
|
const keymap = {
|
||||||
|
@ -121,85 +118,52 @@ export const createCtrl = (initial: State): [Store<State>, { [key: string]: any
|
||||||
[`${mod}-z`]: onUndo,
|
[`${mod}-z`]: onUndo,
|
||||||
[`Shift-${mod}-z`]: onRedo,
|
[`Shift-${mod}-z`]: onRedo,
|
||||||
[`${mod}-y`]: onRedo,
|
[`${mod}-y`]: onRedo,
|
||||||
[`${mod}-m`]: onToggleMarkdown
|
[`${mod}-m`]: toggleMarkdown
|
||||||
}
|
} as unknown as { [key: string]: Command }
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchData = async (): Promise<State> => {
|
const fetchData = async (): Promise<State> => {
|
||||||
const state: State = unwrap(store)
|
|
||||||
const room = window.location.pathname?.slice(1).trim()
|
|
||||||
const args = { room: room || undefined }
|
|
||||||
if (isServer) return
|
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 { default: db } = await import('../db')
|
||||||
const data: string = await db.get('state')
|
const data: string = await db.get('state')
|
||||||
|
console.debug('[editor-ctrl] got stored state from idb')
|
||||||
let parsed
|
let parsed
|
||||||
|
let text = state.text
|
||||||
|
|
||||||
if (data !== undefined) {
|
if (data !== undefined) {
|
||||||
try {
|
try {
|
||||||
parsed = JSON.parse(data)
|
parsed = JSON.parse(data)
|
||||||
if (!parsed) return { ...state, args }
|
if (!parsed) return { ...state, args }
|
||||||
} 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({
|
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,
|
path: parsed.path,
|
||||||
markdown: parsed.markdown,
|
markdown: parsed.markdown,
|
||||||
keymap,
|
keymap,
|
||||||
config: {} as Config
|
config: {} as Config
|
||||||
})
|
}),
|
||||||
|
args,
|
||||||
const nState = {
|
lastModified: parsed.lastModified ? new Date(parsed.lastModified) : new Date()
|
||||||
...parsed,
|
|
||||||
text,
|
|
||||||
extensions,
|
|
||||||
// config,
|
|
||||||
args
|
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
if (nState.lastModified) {
|
throw new ServiceError('invalid_state', data)
|
||||||
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 })
|
const getTheme = (state: State) => ({ theme: state.config.theme })
|
||||||
|
@ -208,8 +172,6 @@ export const createCtrl = (initial: State): [Store<State>, { [key: string]: any
|
||||||
setState({
|
setState({
|
||||||
...newState(),
|
...newState(),
|
||||||
loading: 'initialized',
|
loading: 'initialized',
|
||||||
files: [],
|
|
||||||
fullscreen: store.fullscreen,
|
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
error: undefined,
|
error: undefined,
|
||||||
text: undefined
|
text: undefined
|
||||||
|
@ -217,67 +179,34 @@ export const createCtrl = (initial: State): [Store<State>, { [key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
let data = await fetchData()
|
let state = await fetchData()
|
||||||
|
console.debug('[editor-ctrl] state initiated', state)
|
||||||
try {
|
try {
|
||||||
if (data.args?.room) {
|
if (state.args?.room) {
|
||||||
data = doStartCollab(data)
|
state = doStartCollab(state)
|
||||||
} else if (data.args?.text) {
|
} else if (!state.text) {
|
||||||
data = await doOpenFile(data, { text: JSON.parse(data.args?.text) })
|
|
||||||
} else if (!data.text) {
|
|
||||||
const text = createEmptyText()
|
const text = createEmptyText()
|
||||||
const extensions = createExtensions({
|
const extensions = createExtensions({
|
||||||
config: data.config,
|
config: state.config,
|
||||||
markdown: data.markdown,
|
markdown: state.markdown,
|
||||||
keymap
|
keymap
|
||||||
})
|
})
|
||||||
|
|
||||||
data = { ...data, text, extensions }
|
state = { ...state, text, extensions }
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
data = { ...data, error }
|
state = { ...state, error }
|
||||||
}
|
}
|
||||||
setState({
|
setState({
|
||||||
...data,
|
...state,
|
||||||
config: { ...data.config, ...getTheme(data) },
|
config: { ...state.config, ...getTheme(state) },
|
||||||
loading: 'initialized'
|
loading: 'initialized'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const doOpenFile = async (state: State, file: File): Promise<State> => {
|
|
||||||
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 saveState = debounce(async (state: State) => {
|
||||||
const data = {
|
const data = {
|
||||||
lastModified: state.lastModified,
|
lastModified: state.lastModified,
|
||||||
files: state.files,
|
|
||||||
config: state.config,
|
config: state.config,
|
||||||
path: state.path,
|
path: state.path,
|
||||||
markdown: state.markdown,
|
markdown: state.markdown,
|
||||||
|
@ -322,15 +251,8 @@ export const createCtrl = (initial: State): [Store<State>, { [key: string]: any
|
||||||
let nState = state
|
let nState = state
|
||||||
|
|
||||||
if ((backup && !isEmpty(state.text)) || state.path) {
|
if ((backup && !isEmpty(state.text)) || state.path) {
|
||||||
let files = state.files
|
|
||||||
|
|
||||||
if (!state.error) {
|
|
||||||
files = addToFiles(files, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
nState = {
|
nState = {
|
||||||
...state,
|
...state,
|
||||||
files,
|
|
||||||
lastModified: undefined,
|
lastModified: undefined,
|
||||||
path: undefined,
|
path: undefined,
|
||||||
error: undefined
|
error: undefined
|
||||||
|
@ -357,54 +279,6 @@ export const createCtrl = (initial: State): [Store<State>, { [key: string]: any
|
||||||
window.history.replaceState(null, '', '/')
|
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<Config>) => {
|
const updateConfig = (config: Partial<Config>) => {
|
||||||
const state = unwrap(store)
|
const state = unwrap(store)
|
||||||
const extensions = createExtensions({
|
const extensions = createExtensions({
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { uniqueNamesGenerator, adjectives, animals } from 'unique-names-generator'
|
import { uniqueNamesGenerator, adjectives, animals } from 'unique-names-generator'
|
||||||
import { Awareness } from 'y-protocols/awareness'
|
import { Awareness } from 'y-protocols/awareness'
|
||||||
import { WebrtcProvider } from 'y-webrtc'
|
import { WebrtcProvider } from 'y-webrtc'
|
||||||
import * as Y from 'yjs'
|
import { Doc, XmlFragment } from 'yjs'
|
||||||
import type { Reaction } from '../../../graphql/types.gen'
|
import type { Reaction } from '../../../graphql/types.gen'
|
||||||
import { setReactions } from '../../../stores/editor'
|
import { setReactions } from '../../../stores/editor'
|
||||||
|
|
||||||
export const roomConnect = (room, username = '', keyname = 'collab'): [Y.XmlFragment, WebrtcProvider] => {
|
export const roomConnect = (room, username = '', keyname = 'collab'): [XmlFragment, WebrtcProvider] => {
|
||||||
const ydoc = new Y.Doc()
|
const ydoc = new Doc()
|
||||||
const yarr = ydoc.getArray(keyname + '-reactions')
|
const yarr = ydoc.getArray(keyname + '-reactions')
|
||||||
const yXmlFragment = ydoc.getXmlFragment(keyname)
|
const yXmlFragment = ydoc.getXmlFragment(keyname)
|
||||||
const webrtcOptions = {
|
const webrtcOptions = {
|
||||||
|
|
|
@ -17,10 +17,11 @@ import table from './extension/table'
|
||||||
import collab from './extension/collab'
|
import collab from './extension/collab'
|
||||||
import type { Config, PeerData } from './context'
|
import type { Config, PeerData } from './context'
|
||||||
import selectionMenu from './extension/selection'
|
import selectionMenu from './extension/selection'
|
||||||
|
import type { Command } from 'prosemirror-state'
|
||||||
|
|
||||||
export interface InitOpts {
|
export interface InitOpts {
|
||||||
data?: unknown
|
data?: unknown
|
||||||
keymap?: any
|
keymap?: { [key: string]: Command }
|
||||||
config: Config
|
config: Config
|
||||||
markdown: boolean
|
markdown: boolean
|
||||||
path?: string
|
path?: string
|
||||||
|
|
91
yarn.lock
91
yarn.lock
|
@ -73,24 +73,6 @@
|
||||||
vscode-languageserver-types "^3.17.1"
|
vscode-languageserver-types "^3.17.1"
|
||||||
vscode-uri "^3.0.3"
|
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":
|
"@astrojs/markdown-remark@^1.1.3":
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@astrojs/markdown-remark/-/markdown-remark-1.1.3.tgz#9fa985a532622043f0863c20f01c6ed01eca31e2"
|
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/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@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":
|
"@ljharb/has-package-exports-patterns@^0.0.2":
|
||||||
version "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"
|
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"
|
resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.0.0.tgz#6d888988bb445b34e7c2e1eb97cb88358153221e"
|
||||||
integrity sha512-XDfdemBGJIMAsHHOONHQxEH5dX2kCpE6MGZ1IsNvBuDPBZM3p4EAwAC7ygMjn/1/x+BJX0TK1ara1Zrh7JCFdQ==
|
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"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
|
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
|
||||||
integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==
|
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"
|
resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz#c15cd6180f3e535540739fcc9787bcffb5708cae"
|
||||||
integrity sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==
|
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:
|
hast-util-is-element@^2.0.0:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz#fc0b0dc7cef3895e839b8d66979d57b0338c68f3"
|
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"
|
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
|
||||||
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
|
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:
|
rehype-parse@^8.0.0:
|
||||||
version "8.0.4"
|
version "8.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688"
|
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"
|
hast-util-raw "^7.2.0"
|
||||||
unified "^10.0.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:
|
rehype-stringify@^9.0.0, rehype-stringify@^9.0.3:
|
||||||
version "9.0.3"
|
version "9.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-9.0.3.tgz#70e3bd6d4d29e7acf36b802deed350305d2c3c17"
|
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"
|
hast-util-to-html "^8.0.0"
|
||||||
unified "^10.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:
|
rehype@^12.0.1:
|
||||||
version "12.0.1"
|
version "12.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/rehype/-/rehype-12.0.1.tgz#68a317662576dcaa2565a3952e149d6900096bf6"
|
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"
|
fbjs "^3.0.0"
|
||||||
invariant "^2.2.4"
|
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:
|
remark-gfm@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f"
|
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"
|
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113"
|
||||||
integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw==
|
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:
|
unist-util-is@^5.0.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236"
|
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"
|
resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-1.1.4.tgz#e8a087e58a33a2815f76ea1901c15dec2cb4b432"
|
||||||
integrity sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ==
|
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:
|
unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1:
|
||||||
version "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"
|
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"
|
"@types/unist" "^2.0.0"
|
||||||
unist-util-is "^5.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:
|
unist-util-visit@^4.0.0, unist-util-visit@^4.1.0:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.1.tgz#1c4842d70bd3df6cc545276f5164f933390a9aad"
|
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.1.tgz#1c4842d70bd3df6cc545276f5164f933390a9aad"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user