From 5f47d9c8ce5fe9ff7c7e1332552472274a57bb14 Mon Sep 17 00:00:00 2001 From: Petyo Ivanov Date: Wed, 25 Oct 2023 09:43:55 +0200 Subject: [PATCH] feat: allow passing extensions to the diff/source editors Fixes #141 Fixes #125 Fixes #124 --- package-lock.json | 73 +++++++++++++++++++++--- package.json | 1 + src/examples/toolbar.tsx | 29 +++++++++- src/plugins/diff-source/DiffViewer.tsx | 6 +- src/plugins/diff-source/SourceEditor.tsx | 5 +- src/plugins/diff-source/index.tsx | 15 ++++- 6 files changed, 113 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30c85924..7fa1fe69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,7 @@ "@typescript-eslint/parser": "^6.7.2", "@vitejs/plugin-react": "^4.0.0", "autoprefixer": "^10.4.14", + "cm6-theme-basic-dark": "^0.2.0", "eslint": "^8.41.0", "eslint-config-prettier": "^8.8.0", "eslint-config-standard-with-typescript": "^39.1.0", @@ -4621,7 +4622,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "devOptional": true }, "node_modules/@types/qs": { "version": "6.9.7", @@ -4639,7 +4640,7 @@ "version": "18.2.16", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz", "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4650,7 +4651,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -4659,7 +4660,7 @@ "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "devOptional": true }, "node_modules/@types/semver": { "version": "7.5.2", @@ -6396,6 +6397,18 @@ "node": ">=6" } }, + "node_modules/cm6-theme-basic-dark": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/cm6-theme-basic-dark/-/cm6-theme-basic-dark-0.2.0.tgz", + "integrity": "sha512-+mNNJecRtxS/KkloMDCQF0oTrT6aFGRZTjnBcdT5UG1pcDO4Brq8l1+0KR/8dZ7hub2gOGOzoi3rGFD8GzlH7Q==", + "dev": true, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, "node_modules/cm6-theme-basic-light": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz", @@ -6936,7 +6949,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "devOptional": true }, "node_modules/d": { "version": "1.0.1", @@ -10370,6 +10383,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "peer": true, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/issue-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", @@ -11244,6 +11267,26 @@ "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.12.2.tgz", "integrity": "sha512-Kxavd+ETjxtVwG/hvPd6WZfXD44sLOKe9Vlkwxy7lBQ1qZArS+rZfs+u5iXwXe6tX9f2PIM0u3RHsrCEDDE0fw==" }, + "node_modules/lib0": { + "version": "0.2.87", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.87.tgz", + "integrity": "sha512-TbB63XJixvNToW2IHWAFsCJj9tVnajmwjE14p69i51Rx8byOQd2IP4ourE8v4d7vhyO++nVm1sQk3ePslfbucg==", + "peer": true, + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -17534,7 +17577,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -17554,7 +17596,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -18493,7 +18534,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -21238,6 +21278,23 @@ "node": ">=12" } }, + "node_modules/yjs": { + "version": "13.6.8", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.8.tgz", + "integrity": "sha512-ZPq0hpJQb6f59B++Ngg4cKexDJTvfOgeiv0sBc4sUm8CaBWH7OQC4kcCgrqbjJ/B2+6vO49exvTmYfdlPtcjbg==", + "peer": true, + "dependencies": { + "lib0": "^0.2.74" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/ylru": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz", diff --git a/package.json b/package.json index 74554e04..6f70de32 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "@typescript-eslint/parser": "^6.7.2", "@vitejs/plugin-react": "^4.0.0", "autoprefixer": "^10.4.14", + "cm6-theme-basic-dark": "^0.2.0", "eslint": "^8.41.0", "eslint-config-prettier": "^8.8.0", "eslint-config-standard-with-typescript": "^39.1.0", diff --git a/src/examples/toolbar.tsx b/src/examples/toolbar.tsx index 6e21c7e6..034c2369 100644 --- a/src/examples/toolbar.tsx +++ b/src/examples/toolbar.tsx @@ -25,11 +25,13 @@ import { CreateLink, DiffSourceToggleWrapper, InsertImage, - ListsToggle + ListsToggle, + KitchenSinkToolbar } from '..' import { ALL_PLUGINS, YoutubeDirectiveDescriptor, virtuosoSampleSandpackConfig } from './_boilerplate' import kitchenSinkMarkdown from './assets/kitchen-sink.md?raw' import './dark-editor.css' +import { basicDark } from 'cm6-theme-basic-dark' import type { Story } from '@ladle/react' export const Basics = () => { @@ -50,7 +52,30 @@ ReadOnly.argTypes = { } export const CustomTheming = () => { - return + return ( + }), + listsPlugin(), + quotePlugin(), + headingsPlugin({ allowedHeadingLevels: [1, 2, 3] }), + linkPlugin(), + linkDialogPlugin(), + imagePlugin({ imageAutocompleteSuggestions: ['https://via.placeholder.com/150', 'https://via.placeholder.com/150'] }), + tablePlugin(), + thematicBreakPlugin(), + frontmatterPlugin(), + codeBlockPlugin({ defaultCodeBlockLanguage: 'txt' }), + sandpackPlugin({ sandpackConfig: virtuosoSampleSandpackConfig }), + codeMirrorPlugin({ codeBlockLanguages: { js: 'JavaScript', css: 'CSS', txt: 'text', tsx: 'TypeScript' } }), + directivesPlugin({ directiveDescriptors: [YoutubeDirectiveDescriptor, AdmonitionDirectiveDescriptor] }), + diffSourcePlugin({ viewMode: 'rich-text', diffMarkdown: 'boo', codeMirrorExtensions: [basicDark] }), + markdownShortcutPlugin() + ]} + /> + ) } export const ConditionalToolbar = () => { diff --git a/src/plugins/diff-source/DiffViewer.tsx b/src/plugins/diff-source/DiffViewer.tsx index 661aa750..ccc98e48 100644 --- a/src/plugins/diff-source/DiffViewer.tsx +++ b/src/plugins/diff-source/DiffViewer.tsx @@ -23,6 +23,7 @@ interface CmMergeViewProps { const CmMergeView: React.FC = ({ oldMarkdown, newMarkdown, onUpdate }) => { const cmMergeViewRef = React.useRef(null) + const [cmExtensions] = diffSourcePluginHooks.useEmitterValues('cmExtensions') const ref = React.useCallback( (el: HTMLDivElement | null) => { @@ -40,11 +41,12 @@ const CmMergeView: React.FC = ({ oldMarkdown, newMarkdown, onU gutter: true, a: { doc: oldMarkdown, - extensions: [...COMMON_STATE_CONFIG_EXTENSIONS, EditorState.readOnly.of(true)] + extensions: [...cmExtensions, ...COMMON_STATE_CONFIG_EXTENSIONS, EditorState.readOnly.of(true)] }, b: { doc: newMarkdown, extensions: [ + ...cmExtensions, ...COMMON_STATE_CONFIG_EXTENSIONS, EditorView.updateListener.of(({ state }) => { const md = state.doc.toString() @@ -58,7 +60,7 @@ const CmMergeView: React.FC = ({ oldMarkdown, newMarkdown, onU cmMergeViewRef.current = null } }, - [newMarkdown, oldMarkdown, onUpdate] + [newMarkdown, oldMarkdown, onUpdate, cmExtensions] ) return
diff --git a/src/plugins/diff-source/SourceEditor.tsx b/src/plugins/diff-source/SourceEditor.tsx index b495bbdf..57cdb4e0 100644 --- a/src/plugins/diff-source/SourceEditor.tsx +++ b/src/plugins/diff-source/SourceEditor.tsx @@ -11,6 +11,7 @@ export const COMMON_STATE_CONFIG_EXTENSIONS: Extension[] = [basicSetup, basicLig export const SourceEditor = () => { const [markdown, readOnly] = corePluginHooks.useEmitterValues('markdown', 'readOnly') + const [cmExtensions] = diffSourcePluginHooks.useEmitterValues('cmExtensions') const updateMarkdown = diffSourcePluginHooks.usePublisher('markdownSourceEditorValue') const editorViewRef = React.useRef(null) @@ -18,6 +19,8 @@ export const SourceEditor = () => { (el: HTMLDivElement | null) => { if (el !== null) { const extensions = [ + // custom extensions should come first so that you can override the default extensions + ...cmExtensions, ...COMMON_STATE_CONFIG_EXTENSIONS, EditorView.updateListener.of(({ state }) => { updateMarkdown(state.doc.toString()) @@ -36,7 +39,7 @@ export const SourceEditor = () => { editorViewRef.current = null } }, - [markdown, readOnly, updateMarkdown] + [markdown, readOnly, updateMarkdown, cmExtensions] ) return
diff --git a/src/plugins/diff-source/index.tsx b/src/plugins/diff-source/index.tsx index 3b920d04..67164076 100644 --- a/src/plugins/diff-source/index.tsx +++ b/src/plugins/diff-source/index.tsx @@ -1,3 +1,4 @@ +import { Extension } from '@codemirror/state' import { realmPlugin, system } from '../../gurx' import { coreSystem } from '../core' import { DiffSourceWrapper } from './DiffSourceWrapper' @@ -10,6 +11,7 @@ export const diffSourceSystem = system( (r, [{ markdown, setMarkdown }]) => { const diffMarkdown = r.node('') const markdownSourceEditorValue = r.node('') + const cmExtensions = r.node([]) r.link(markdown, markdownSourceEditorValue) const viewMode = r.node('rich-text') @@ -34,11 +36,17 @@ export const diffSourceSystem = system( } } ) - return { viewMode, diffMarkdown, markdownSourceEditorValue } + return { viewMode, diffMarkdown, markdownSourceEditorValue, cmExtensions } }, [coreSystem] ) +export interface DiffSourcePluginParams { + viewMode?: ViewMode + diffMarkdown?: string + codeMirrorExtensions?: Extension[] +} + export const [ /** @internal */ diffSourcePlugin, @@ -48,11 +56,12 @@ export const [ id: 'diff-source', systemSpec: diffSourceSystem, - applyParamsToSystem(r, params?: { viewMode?: ViewMode; diffMarkdown?: string }) { + applyParamsToSystem(r, params?: DiffSourcePluginParams) { r.pubKey('viewMode', params?.viewMode || 'rich-text') }, - init(r, params) { + init(r, params?: DiffSourcePluginParams) { r.pubKey('diffMarkdown', params?.diffMarkdown || '') + r.pubKey('cmExtensions', params?.codeMirrorExtensions || []) r.pubKey('addEditorWrapper', DiffSourceWrapper) } })