update codemirror

This commit is contained in:
熊潇 2025-06-03 19:14:50 +08:00
parent 43980ae43c
commit 737014b90f
6 changed files with 144 additions and 26 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@kevisual/codemirror", "name": "@kevisual/codemirror",
"version": "0.0.8", "version": "0.0.12",
"description": "", "description": "",
"main": "dist/editor.js", "main": "dist/editor.js",
"private": false, "private": false,
@ -27,6 +27,7 @@
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.36.7", "@codemirror/view": "^6.36.7",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"eventemitter3": "^5.0.1",
"prettier": "^3.5.3" "prettier": "^3.5.3"
}, },
"devDependencies": { "devDependencies": {
@ -52,6 +53,10 @@
"./json": { "./json": {
"import": "./dist/editor.json.js", "import": "./dist/editor.json.js",
"types": "./dist/editor.json.d.ts" "types": "./dist/editor.json.d.ts"
},
"./utils": {
"import": "./dist/editor.utils.js",
"types": "./dist/editor.utils.d.ts"
} }
} }
} }

View File

@ -1,7 +1,7 @@
import { nodeResolve } from '@rollup/plugin-node-resolve'; import { nodeResolve } from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript'; import typescript from '@rollup/plugin-typescript';
const entrys = ['editor', 'editor.json', 'editor.base']; const entrys = ['editor', 'editor.json', 'editor.base', 'editor.utils'];
const configs = entrys.map((entry) => ({ const configs = entrys.map((entry) => ({
input: `./src/${entry}.ts`, // 修改输入文件为 TypeScript 文件 input: `./src/${entry}.ts`, // 修改输入文件为 TypeScript 文件
output: { output: {

View File

@ -1,10 +1,18 @@
import { EditorView, basicSetup } from 'codemirror'; import { basicSetup } from 'codemirror';
import { EditorView } from '@codemirror/view';
import { StateEffect } from '@codemirror/state';
import { ViewUpdate } from '@codemirror/view';
import { formatKeymap } from './extensions/tab'; import { formatKeymap } from './extensions/tab';
let editor: EditorView = null; import EventEmitter from 'eventemitter3';
export type CodeEditor = EditorView & {
emitter?: EventEmitter;
};
let editor: CodeEditor = null;
export type EditorOptions = { export type EditorOptions = {
extensions?: any[]; extensions?: any[];
hasBasicSetup?: boolean; hasBasicSetup?: boolean;
onChange?: (content: string) => void;
}; };
/** /**
* *
@ -24,12 +32,15 @@ const createEditorInstance = (el?: HTMLDivElement, opts?: EditorOptions) => {
if (hasBaseicSetup) { if (hasBaseicSetup) {
extensions.unshift(basicSetup); extensions.unshift(basicSetup);
} }
const emitter = new EventEmitter();
createOnChangeListener(emitter, opts.onChange).appendTo(extensions);
editor = new EditorView({ editor = new EditorView({
extensions: extensions, extensions: extensions,
parent: el || document.body, parent: el || document.body,
}); });
editor.emitter = emitter;
editor.dom.style.height = '100%'; editor.dom.style.height = '100%';
return editor; return editor as CodeEditor;
}; };
/** /**
@ -44,13 +55,38 @@ export const createEditor = (el: HTMLDivElement, opts?: EditorOptions) => {
extensions.unshift(basicSetup); extensions.unshift(basicSetup);
} }
extensions.push(formatKeymap); extensions.push(formatKeymap);
const emitter = new EventEmitter();
createOnChangeListener(emitter, opts.onChange).appendTo(extensions);
const editor = new EditorView({ const editor = new EditorView({
extensions, extensions,
parent: el || document.body, parent: el || document.body,
}); }) as CodeEditor;
editor.emitter = emitter;
editor.dom.style.height = '100%'; editor.dom.style.height = '100%';
return editor;
return editor as CodeEditor;
}; };
export const getEditor = () => editor; export const getEditor = () => editor;
export { editor, createEditorInstance }; export { editor, createEditorInstance };
export const createOnChangeListener = (emitter: EventEmitter, callback?: (content: string) => void) => {
const listener = EditorView.updateListener.of((update: ViewUpdate) => {
if (update.docChanged) {
const editor = update.view;
if (callback) {
callback(editor.state.doc.toString());
}
// 触发自定义事件
emitter.emit('change', editor.state.doc.toString());
}
});
// 返回监听器配置,而不是直接应用它
return {
extension: listener,
appendTo: (ext: any[]) => {
ext.push(listener);
},
};
};

View File

@ -1,11 +1,18 @@
import { EditorView, basicSetup } from 'codemirror'; import { basicSetup } from 'codemirror';
import { EditorView } from '@codemirror/view';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { json } from '@codemirror/lang-json'; import { json } from '@codemirror/lang-json';
import { html } from '@codemirror/lang-html'; import { html } from '@codemirror/lang-html';
import { markdown } from '@codemirror/lang-markdown'; import { markdown } from '@codemirror/lang-markdown';
import { css } from '@codemirror/lang-css'; import { css } from '@codemirror/lang-css';
import { formatKeymap } from './extensions/tab'; import { formatKeymap } from './extensions/tab';
let editor: EditorView = null; import { createOnChangeListener } from './editor.base';
import EventEmitter from 'eventemitter3';
export type CodeEditor = EditorView & {
emitter?: EventEmitter;
};
let editor: CodeEditor = null;
type CreateOpts = { type CreateOpts = {
jsx?: boolean; jsx?: boolean;
@ -14,6 +21,7 @@ type CreateOpts = {
hasBasicSetup?: boolean; hasBasicSetup?: boolean;
extensions?: any[]; extensions?: any[];
hasKeymap?: boolean; hasKeymap?: boolean;
onChange?: (content: string) => void;
}; };
/** /**
* *
@ -54,12 +62,15 @@ const createEditorInstance = (el?: HTMLDivElement, opts?: CreateOpts) => {
extensions.push(markdown()); extensions.push(markdown());
break; break;
} }
const emitter = new EventEmitter();
createOnChangeListener(emitter, opts.onChange).appendTo(extensions);
editor = new EditorView({ editor = new EditorView({
extensions: extensions, extensions: extensions,
parent: el || document.body, parent: el || document.body,
}); });
editor.dom.style.height = '100%'; editor.dom.style.height = '100%';
return editor; editor.emitter = emitter;
return editor as CodeEditor;
}; };
/** /**
@ -95,12 +106,15 @@ export const createEditor = (el: HTMLDivElement, opts?: CreateOpts) => {
extensions.push(markdown()); extensions.push(markdown());
break; break;
} }
const emitter = new EventEmitter();
createOnChangeListener(emitter, opts.onChange).appendTo(extensions);
const editor = new EditorView({ const editor = new EditorView({
extensions: extensions, extensions: extensions,
parent: el || document.body, parent: el || document.body,
}); }) as CodeEditor;
editor.dom.style.height = '100%'; editor.dom.style.height = '100%';
return editor; editor.emitter = emitter;
return editor as CodeEditor;
}; };
export { editor, createEditorInstance }; export { editor, createEditorInstance };

View File

@ -0,0 +1,59 @@
import { EditorView } from '@codemirror/view';
import { CodeEditor } from './editor.base';
type ChainOpts = {
editor?: CodeEditor;
};
export class Chain {
editor: CodeEditor | EditorView;
constructor(opts?: ChainOpts) {
this.editor = opts?.editor;
}
getEditor() {
return this.editor;
}
getContent() {
return this.editor?.state.doc.toString() || '';
}
setContent(content: string) {
if (this.editor) {
this.editor.dispatch({
changes: { from: 0, to: this.editor.state.doc.length, insert: content },
});
}
return this;
}
setEditor(editor: EditorView) {
this.editor = editor;
return this;
}
clearEditor() {
if (this.editor) {
this.editor.dispatch({
changes: { from: 0, to: this.editor.state.doc.length, insert: '' },
});
}
return this;
}
destroy() {
if (this.editor) {
this.editor.destroy();
this.editor = null;
}
return this;
}
static create(opts?: ChainOpts) {
return new Chain(opts);
}
setOnChange(callback: (content: string) => void) {
if (this.editor) {
const editor = this.editor as CodeEditor;
if (editor.emitter) {
editor.emitter.on('change', callback);
return () => {
editor.emitter.off('change', callback);
};
}
}
return () => {};
}
}

32
pnpm-lock.yaml generated
View File

@ -53,7 +53,7 @@ importers:
version: 5.7.12(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(rollup@4.40.2)(terser@5.36.0)(typescript@5.8.3)(yaml@2.5.1) version: 5.7.12(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(rollup@4.40.2)(terser@5.36.0)(typescript@5.8.3)(yaml@2.5.1)
tailwind-merge: tailwind-merge:
specifier: ^3.2.0 specifier: ^3.2.0
version: 3.2.0 version: 3.3.0
tailwindcss: tailwindcss:
specifier: ^4.1.6 specifier: ^4.1.6
version: 4.1.6 version: 4.1.6
@ -95,7 +95,7 @@ importers:
version: 11.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 11.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
tailwind-merge: tailwind-merge:
specifier: ^3.2.0 specifier: ^3.2.0
version: 3.2.0 version: 3.3.0
devDependencies: devDependencies:
'@astrojs/mdx': '@astrojs/mdx':
specifier: ^4.2.6 specifier: ^4.2.6
@ -135,7 +135,7 @@ importers:
version: 1.2.9 version: 1.2.9
packages/codemirror: packages/codemirror:
devDependencies: dependencies:
'@codemirror/autocomplete': '@codemirror/autocomplete':
specifier: ^6.18.6 specifier: ^6.18.6
version: 6.18.6 version: 6.18.6
@ -163,18 +163,22 @@ importers:
'@codemirror/view': '@codemirror/view':
specifier: ^6.36.7 specifier: ^6.36.7
version: 6.36.7 version: 6.36.7
codemirror:
specifier: ^6.0.1
version: 6.0.1
eventemitter3:
specifier: ^5.0.1
version: 5.0.1
prettier:
specifier: ^3.5.3
version: 3.5.3
devDependencies:
'@rollup/plugin-node-resolve': '@rollup/plugin-node-resolve':
specifier: ^16.0.1 specifier: ^16.0.1
version: 16.0.1(rollup@4.40.2) version: 16.0.1(rollup@4.40.2)
'@rollup/plugin-typescript': '@rollup/plugin-typescript':
specifier: ^12.1.2 specifier: ^12.1.2
version: 12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.3) version: 12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.3)
codemirror:
specifier: ^6.0.1
version: 6.0.1
prettier:
specifier: ^3.5.3
version: 3.5.3
rollup: rollup:
specifier: ^4.40.2 specifier: ^4.40.2
version: 4.40.2 version: 4.40.2
@ -271,8 +275,8 @@ importers:
specifier: ^16.1.0 specifier: ^16.1.0
version: 16.1.0 version: 16.1.0
tailwind-merge: tailwind-merge:
specifier: ^3.2.0 specifier: ^3.3.0
version: 3.2.0 version: 3.3.0
tailwindcss: tailwindcss:
specifier: ^4.1.6 specifier: ^4.1.6
version: 4.1.6 version: 4.1.6
@ -4144,8 +4148,8 @@ packages:
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
hasBin: true hasBin: true
tailwind-merge@3.2.0: tailwind-merge@3.3.0:
resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} resolution: {integrity: sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==}
tailwindcss-animate@1.0.7: tailwindcss-animate@1.0.7:
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
@ -9141,7 +9145,7 @@ snapshots:
csso: 5.0.5 csso: 5.0.5
picocolors: 1.1.1 picocolors: 1.1.1
tailwind-merge@3.2.0: {} tailwind-merge@3.3.0: {}
tailwindcss-animate@1.0.7(tailwindcss@4.1.6): tailwindcss-animate@1.0.7(tailwindcss@4.1.6):
dependencies: dependencies: