This commit is contained in:
tonyrewin 2022-10-21 01:02:09 +03:00
parent df28889248
commit 92906931da
3 changed files with 86 additions and 82 deletions

View File

@ -1,59 +1,52 @@
import { keymap } from 'prosemirror-keymap'
import { ProseMirrorExtension } from './helpers'
import { Schema } from 'prosemirror-model'
import base from './extension/base'
import markdown from './extension/markdown'
import link from './extension/link'
// import scroll from './prosemirror/extension/scroll'
import todoList from './extension/todo-list'
import code from './extension/code'
import strikethrough from './extension/strikethrough'
import placeholder from './extension/placeholder'
// import menu from './extension/menu' // import menu from './extension/menu'
import image from './extension/image' // import scroll from './prosemirror/extension/scroll'
import { keymap } from 'prosemirror-keymap'
import type { ProseMirrorExtension } from './helpers'
import { Schema } from 'prosemirror-model'
import { t } from '../../../utils/intl'
import base from './extension/base'
import code from './extension/code'
import dragHandle from './extension/drag-handle' import dragHandle from './extension/drag-handle'
import image from './extension/image'
import link from './extension/link'
import markdown from './extension/markdown'
import pasteMarkdown from './extension/paste-markdown' import pasteMarkdown from './extension/paste-markdown'
import table from './extension/table' import table from './extension/table'
import collab from './extension/collab' import collab from './extension/collab'
import { Config, YOptions } from '../store/context' import type { Config, YOptions } from '../store/context'
import selectionMenu from './extension/selection' import selectionMenu from './extension/selection'
import type { Command } from 'prosemirror-state'
import placeholder from './extension/placeholder'
import todoList from './extension/todo-list'
import strikethrough from './extension/strikethrough'
import scrollPlugin from './extension/scroll'
interface Props { interface ExtensionsProps {
data?: unknown; data?: unknown
keymap?: any; keymap?: { [key: string]: Command }
config: Config; config: Config
markdown: boolean; markdown: boolean
path?: string; path?: string
y?: YOptions; y?: YOptions
schema?: Schema; schema?: Schema
collab?: any
typewriterMode?: boolean
} }
const customKeymap = (props: Props): ProseMirrorExtension => ({ const customKeymap = (props: ExtensionsProps): ProseMirrorExtension => ({
plugins: (prev) => (props.keymap ? [...prev, keymap(props.keymap)] : prev) plugins: (prev) => (props.keymap ? [...prev, keymap(props.keymap)] : prev)
}) })
/*
const codeMirrorKeymap = (props: Props) => {
const keys = []
for (const key in props.keymap) {
keys.push({key: key, run: props.keymap[key]})
}
return cmKeymap.of(keys) export const createExtensions = (props: ExtensionsProps): ProseMirrorExtension[] => {
} const eee = [
*/ placeholder(t('Just start typing...')),
export const createExtensions = (props: Props): ProseMirrorExtension[] =>
props.markdown
? [
placeholder('Просто начните...'),
customKeymap(props), customKeymap(props),
base(props.markdown), base(props.markdown),
collab(props.y),
selectionMenu()
]
: [
selectionMenu(), selectionMenu(),
customKeymap(props), scrollPlugin(props.config?.typewriterMode)
base(props.markdown), ]
if (props.markdown) {
eee.push(
markdown(), markdown(),
todoList(), todoList(),
dragHandle(), dragHandle(),
@ -62,9 +55,7 @@ export const createExtensions = (props: Props): ProseMirrorExtension[] =>
link(), link(),
table(), table(),
image(props.path), image(props.path),
pasteMarkdown(), pasteMarkdown()
collab(props.y)
// scroll(props.config.typewriterMode),
/* /*
codeBlock({ codeBlock({
theme: codeTheme(props.config), theme: codeTheme(props.config),
@ -74,7 +65,11 @@ export const createExtensions = (props: Props): ProseMirrorExtension[] =>
extensions: () => [codeMirrorKeymap(props)], extensions: () => [codeMirrorKeymap(props)],
}), }),
*/ */
] )
}
if (props.collab?.room) eee.push(collab(props.y))
return eee
}
export const createEmptyText = () => ({ export const createEmptyText = () => ({
doc: { doc: {
@ -88,7 +83,7 @@ export const createEmptyText = () => ({
} }
}) })
export const createSchema = (props: Props) => { export const createSchema = (props: ExtensionsProps) => {
const extensions = createExtensions({ const extensions = createExtensions({
config: props.config, config: props.config,
markdown: props.markdown, markdown: props.markdown,

View File

@ -1,12 +1,9 @@
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 { EditorState } from 'prosemirror-state' import type { 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 * as Y from 'yjs'
import { undo as yUndo, redo as yRedo } from 'y-prosemirror' import { undo as yUndo, redo as yRedo } from 'y-prosemirror'
import { WebrtcProvider } from 'y-webrtc'
import { uniqueNamesGenerator, adjectives, animals } from 'unique-names-generator'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { createSchema, createExtensions, createEmptyText } from '../prosemirror/setup' import { createSchema, createExtensions, createEmptyText } from '../prosemirror/setup'
import { State, File, Config, ServiceError, newState } from './context' import { State, File, Config, ServiceError, newState } from './context'
@ -14,11 +11,10 @@ import { mod } from '../env'
import { serialize, createMarkdownParser } from '../markdown' import { serialize, createMarkdownParser } from '../markdown'
import db from '../db' import db from '../db'
import { isEmpty, isInitialized } from '../prosemirror/helpers' import { isEmpty, isInitialized } from '../prosemirror/helpers'
import { Awareness } from 'y-protocols/awareness'
const isText = (x: any) => x && x.doc && x.selection const isText = (x) => x && x.doc && x.selection
const isState = (x: any) => typeof x.lastModified !== 'string' && Array.isArray(x.files) const isState = (x) => typeof x.lastModified !== 'string' && Array.isArray(x.files)
const isFile = (x: any): boolean => x && (x.text || x.path) const isFile = (x): boolean => x && (x.text || x.path)
export const createCtrl = (initial: State): [Store<State>, any] => { export const createCtrl = (initial: State): [Store<State>, any] => {
const [store, setState] = createStore(initial) const [store, setState] = createStore(initial)
@ -131,13 +127,14 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
const fetchData = async (): Promise<State> => { const fetchData = async (): Promise<State> => {
const state: State = unwrap(store) const state: State = unwrap(store)
const room = window.location.pathname?.slice(1).trim() const room = window.location.pathname?.slice(1).trim()
const args = { room: room ? room : undefined } const args = { room: room ?? undefined }
const data = await db.get('state') const data = await db.get('state')
let parsed: any let parsed: any
if (data !== undefined) { if (data !== undefined) {
try { try {
parsed = JSON.parse(data) parsed = JSON.parse(data)
} catch (err) { } catch (error) {
console.error(error)
throw new ServiceError('invalid_state', data) throw new ServiceError('invalid_state', data)
} }
} }
@ -162,7 +159,7 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
config: undefined config: undefined
}) })
const newState = { const newst = {
...parsed, ...parsed,
text, text,
extensions, extensions,
@ -170,8 +167,8 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
args args
} }
if (newState.lastModified) { if (newst.lastModified) {
newState.lastModified = new Date(newState.lastModified) newst.lastModified = new Date(newst.lastModified)
} }
for (const file of parsed.files) { for (const file of parsed.files) {
@ -180,11 +177,11 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
} }
} }
if (!isState(newState)) { if (!isState(newst)) {
throw new ServiceError('invalid_state', newState) throw new ServiceError('invalid_state', newState)
} }
return newState return newst
} }
const getTheme = (state: State) => ({ theme: state.config.theme }) const getTheme = (state: State) => ({ theme: state.config.theme })
@ -222,7 +219,7 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
const extensions = createExtensions({ const extensions = createExtensions({
config: data.config ?? store.config, config: data.config ?? store.config,
markdown: data.markdown ?? store.markdown, markdown: data.markdown ?? store.markdown,
keymap: keymap keymap
}) })
data = { ...data, text, extensions } data = { ...data, text, extensions }
} }
@ -284,14 +281,14 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
y: { type, provider } y: { type, provider }
}) })
let newState = state let newst = state
if ((backup && !isEmpty(state.text)) || state.path) { if ((backup && !isEmpty(state.text)) || state.path) {
let files = state.files let files = state.files
if (!state.error) { if (!state.error) {
files = addToFiles(files, state) files = addToFiles(files, state)
} }
newState = { newst = {
...state, ...state,
files, files,
lastModified: undefined, lastModified: undefined,
@ -301,7 +298,7 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
} }
return { return {
...newState, ...newst,
extensions, extensions,
collab: { started: true, room, y: { type, provider } } collab: { started: true, room, y: { type, provider } }
} }
@ -356,7 +353,7 @@ export const createCtrl = (initial: State): [Store<State>, any] => {
config: state.config, config: state.config,
markdown, markdown,
path: state.path, path: state.path,
keymap: keymap, keymap,
y: state.collab?.y y: state.collab?.y
}) })

View File

@ -1,8 +1,10 @@
import { createContext, useContext } from 'solid-js' import { createContext, useContext } from 'solid-js'
import { Store } from 'solid-js/store' import type { Store } from 'solid-js/store'
import { XmlFragment } from 'yjs' import type { XmlFragment } from 'yjs'
import { WebrtcProvider } from 'y-webrtc' import type { WebrtcProvider } from 'y-webrtc'
import { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers' import type { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers'
import type { Command, EditorState } from 'prosemirror-state'
import type { EditorView } from 'prosemirror-view'
export interface Args { export interface Args {
cwd?: string; cwd?: string;
@ -50,7 +52,7 @@ export type LoadingType = 'loading' | 'initialized'
export interface State { export interface State {
text?: ProseMirrorState; text?: ProseMirrorState;
editorView?: any; editorView?: EditorView;
extensions?: ProseMirrorExtension[]; extensions?: ProseMirrorExtension[];
markdown?: boolean; markdown?: boolean;
lastModified?: Date; lastModified?: Date;
@ -62,6 +64,16 @@ export interface State {
collab?: Collab; collab?: Collab;
path?: string; path?: string;
args?: Args; args?: Args;
keymap?: { [key: string]: Command; }
}
export interface Draft {
body?: string
lastModified?: Date
text?: { doc: EditorState['doc']; selection: { type: string; anchor: number; head: number } }
path?: string
markdown?: boolean
extensions?: ProseMirrorExtension[]
} }
export interface File { export interface File {