diff --git a/src/components/Editor/prosemirror/setup.ts b/src/components/Editor/prosemirror/setup.ts index 275093b7..dca7ca4c 100644 --- a/src/components/Editor/prosemirror/setup.ts +++ b/src/components/Editor/prosemirror/setup.ts @@ -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 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 image from './extension/image' +import link from './extension/link' +import markdown from './extension/markdown' import pasteMarkdown from './extension/paste-markdown' import table from './extension/table' import collab from './extension/collab' -import { Config, YOptions } from '../store/context' +import type { Config, YOptions } from '../store/context' 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 { - data?: unknown; - keymap?: any; - config: Config; - markdown: boolean; - path?: string; - y?: YOptions; - schema?: Schema; +interface ExtensionsProps { + data?: unknown + keymap?: { [key: string]: Command } + config: Config + markdown: boolean + path?: string + y?: YOptions + 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) }) -/* -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: Props): ProseMirrorExtension[] => - props.markdown - ? [ - placeholder('Просто начните...'), - customKeymap(props), - base(props.markdown), - collab(props.y), - selectionMenu() - ] - : [ - selectionMenu(), - customKeymap(props), - base(props.markdown), +export const createExtensions = (props: ExtensionsProps): ProseMirrorExtension[] => { + const eee = [ + placeholder(t('Just start typing...')), + customKeymap(props), + base(props.markdown), + selectionMenu(), + scrollPlugin(props.config?.typewriterMode) + ] + if (props.markdown) { + eee.push( markdown(), todoList(), dragHandle(), @@ -62,19 +55,21 @@ export const createExtensions = (props: Props): ProseMirrorExtension[] => link(), table(), image(props.path), - pasteMarkdown(), - collab(props.y) - // scroll(props.config.typewriterMode), + pasteMarkdown() /* - codeBlock({ - theme: codeTheme(props.config), - typewriterMode: props.config.typewriterMode, - fontSize: props.config.fontSize, - prettier: props.config.prettier, - extensions: () => [codeMirrorKeymap(props)], - }), - */ - ] + codeBlock({ + theme: codeTheme(props.config), + typewriterMode: props.config.typewriterMode, + fontSize: props.config.fontSize, + prettier: props.config.prettier, + extensions: () => [codeMirrorKeymap(props)], + }), + */ + ) + } + if (props.collab?.room) eee.push(collab(props.y)) + return eee +} export const createEmptyText = () => ({ doc: { @@ -88,7 +83,7 @@ export const createEmptyText = () => ({ } }) -export const createSchema = (props: Props) => { +export const createSchema = (props: ExtensionsProps) => { const extensions = createExtensions({ config: props.config, markdown: props.markdown, diff --git a/src/components/Editor/store/actions.ts b/src/components/Editor/store/actions.ts index 0bb620d1..72346794 100644 --- a/src/components/Editor/store/actions.ts +++ b/src/components/Editor/store/actions.ts @@ -1,12 +1,9 @@ import { Store, createStore, unwrap } from 'solid-js/store' import { v4 as uuidv4 } from 'uuid' -import { EditorState } from 'prosemirror-state' +import type { EditorState } from 'prosemirror-state' import { undo, redo } from 'prosemirror-history' import { selectAll, deleteSelection } from 'prosemirror-commands' -import * as Y from 'yjs' 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 { createSchema, createExtensions, createEmptyText } from '../prosemirror/setup' import { State, File, Config, ServiceError, newState } from './context' @@ -14,11 +11,10 @@ import { mod } from '../env' import { serialize, createMarkdownParser } from '../markdown' import db from '../db' import { isEmpty, isInitialized } from '../prosemirror/helpers' -import { Awareness } from 'y-protocols/awareness' -const isText = (x: any) => x && x.doc && x.selection -const isState = (x: any) => typeof x.lastModified !== 'string' && Array.isArray(x.files) -const isFile = (x: any): boolean => x && (x.text || x.path) +const isText = (x) => x && x.doc && x.selection +const isState = (x) => typeof x.lastModified !== 'string' && Array.isArray(x.files) +const isFile = (x): boolean => x && (x.text || x.path) export const createCtrl = (initial: State): [Store, any] => { const [store, setState] = createStore(initial) @@ -131,13 +127,14 @@ export const createCtrl = (initial: State): [Store, any] => { const fetchData = async (): Promise => { const state: State = unwrap(store) 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') let parsed: any if (data !== undefined) { try { parsed = JSON.parse(data) - } catch (err) { + } catch (error) { + console.error(error) throw new ServiceError('invalid_state', data) } } @@ -162,7 +159,7 @@ export const createCtrl = (initial: State): [Store, any] => { config: undefined }) - const newState = { + const newst = { ...parsed, text, extensions, @@ -170,8 +167,8 @@ export const createCtrl = (initial: State): [Store, any] => { args } - if (newState.lastModified) { - newState.lastModified = new Date(newState.lastModified) + if (newst.lastModified) { + newst.lastModified = new Date(newst.lastModified) } for (const file of parsed.files) { @@ -180,11 +177,11 @@ export const createCtrl = (initial: State): [Store, any] => { } } - if (!isState(newState)) { + if (!isState(newst)) { throw new ServiceError('invalid_state', newState) } - return newState + return newst } const getTheme = (state: State) => ({ theme: state.config.theme }) @@ -222,7 +219,7 @@ export const createCtrl = (initial: State): [Store, any] => { const extensions = createExtensions({ config: data.config ?? store.config, markdown: data.markdown ?? store.markdown, - keymap: keymap + keymap }) data = { ...data, text, extensions } } @@ -284,14 +281,14 @@ export const createCtrl = (initial: State): [Store, any] => { y: { type, provider } }) - let newState = state + let newst = state if ((backup && !isEmpty(state.text)) || state.path) { let files = state.files if (!state.error) { files = addToFiles(files, state) } - newState = { + newst = { ...state, files, lastModified: undefined, @@ -301,7 +298,7 @@ export const createCtrl = (initial: State): [Store, any] => { } return { - ...newState, + ...newst, extensions, collab: { started: true, room, y: { type, provider } } } @@ -356,7 +353,7 @@ export const createCtrl = (initial: State): [Store, any] => { config: state.config, markdown, path: state.path, - keymap: keymap, + keymap, y: state.collab?.y }) diff --git a/src/components/Editor/store/context.ts b/src/components/Editor/store/context.ts index 1d0aa382..2d07d1e8 100644 --- a/src/components/Editor/store/context.ts +++ b/src/components/Editor/store/context.ts @@ -1,8 +1,10 @@ import { createContext, useContext } from 'solid-js' -import { Store } from 'solid-js/store' -import { XmlFragment } from 'yjs' -import { WebrtcProvider } from 'y-webrtc' -import { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers' +import type { Store } from 'solid-js/store' +import type { XmlFragment } from 'yjs' +import type { WebrtcProvider } from 'y-webrtc' +import type { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers' +import type { Command, EditorState } from 'prosemirror-state' +import type { EditorView } from 'prosemirror-view' export interface Args { cwd?: string; @@ -50,7 +52,7 @@ export type LoadingType = 'loading' | 'initialized' export interface State { text?: ProseMirrorState; - editorView?: any; + editorView?: EditorView; extensions?: ProseMirrorExtension[]; markdown?: boolean; lastModified?: Date; @@ -62,6 +64,16 @@ export interface State { collab?: Collab; path?: string; 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 {