@jvs-milkdown/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/lib/__internal__/index.d.ts +3 -0
- package/lib/__internal__/index.d.ts.map +1 -0
- package/lib/__internal__/remark-handlers.d.ts +3 -0
- package/lib/__internal__/remark-handlers.d.ts.map +1 -0
- package/lib/__internal__/utils.d.ts +3 -0
- package/lib/__internal__/utils.d.ts.map +1 -0
- package/lib/editor/editor.d.ts +28 -0
- package/lib/editor/editor.d.ts.map +1 -0
- package/lib/editor/index.d.ts +2 -0
- package/lib/editor/index.d.ts.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +676 -0
- package/lib/index.js.map +1 -0
- package/lib/internal-plugin/atoms.d.ts +22 -0
- package/lib/internal-plugin/atoms.d.ts.map +1 -0
- package/lib/internal-plugin/commands.d.ts +38 -0
- package/lib/internal-plugin/commands.d.ts.map +1 -0
- package/lib/internal-plugin/config.d.ts +5 -0
- package/lib/internal-plugin/config.d.ts.map +1 -0
- package/lib/internal-plugin/editor-state.d.ts +22 -0
- package/lib/internal-plugin/editor-state.d.ts.map +1 -0
- package/lib/internal-plugin/editor-view.d.ts +13 -0
- package/lib/internal-plugin/editor-view.d.ts.map +1 -0
- package/lib/internal-plugin/index.d.ts +12 -0
- package/lib/internal-plugin/index.d.ts.map +1 -0
- package/lib/internal-plugin/init.d.ts +5 -0
- package/lib/internal-plugin/init.d.ts.map +1 -0
- package/lib/internal-plugin/keymap.d.ts +22 -0
- package/lib/internal-plugin/keymap.d.ts.map +1 -0
- package/lib/internal-plugin/keymap.test.d.ts +2 -0
- package/lib/internal-plugin/keymap.test.d.ts.map +1 -0
- package/lib/internal-plugin/parser.d.ts +7 -0
- package/lib/internal-plugin/parser.d.ts.map +1 -0
- package/lib/internal-plugin/paste-rule.d.ts +12 -0
- package/lib/internal-plugin/paste-rule.d.ts.map +1 -0
- package/lib/internal-plugin/schema.d.ts +10 -0
- package/lib/internal-plugin/schema.d.ts.map +1 -0
- package/lib/internal-plugin/serializer.d.ts +7 -0
- package/lib/internal-plugin/serializer.d.ts.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +45 -0
- package/src/__internal__/index.ts +2 -0
- package/src/__internal__/remark-handlers.ts +48 -0
- package/src/__internal__/utils.ts +14 -0
- package/src/editor/editor.ts +300 -0
- package/src/editor/index.ts +1 -0
- package/src/index.ts +2 -0
- package/src/internal-plugin/atoms.ts +69 -0
- package/src/internal-plugin/commands.ts +176 -0
- package/src/internal-plugin/config.ts +34 -0
- package/src/internal-plugin/editor-state.ts +140 -0
- package/src/internal-plugin/editor-view.ts +166 -0
- package/src/internal-plugin/index.ts +11 -0
- package/src/internal-plugin/init.ts +78 -0
- package/src/internal-plugin/keymap.test.ts +136 -0
- package/src/internal-plugin/keymap.ts +167 -0
- package/src/internal-plugin/parser.ts +51 -0
- package/src/internal-plugin/paste-rule.ts +53 -0
- package/src/internal-plugin/schema.ts +88 -0
- package/src/internal-plugin/serializer.ts +61 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
import { Clock, Container, Ctx, createSlice, createTimer } from "@jvs-milkdown/ctx";
|
|
2
|
+
import remarkParse from "remark-parse";
|
|
3
|
+
import remarkStringify from "remark-stringify";
|
|
4
|
+
import { unified } from "unified";
|
|
5
|
+
import { callCommandBeforeEditorView, ctxCallOutOfScope, docTypeError } from "@jvs-milkdown/exception";
|
|
6
|
+
import { baseKeymap, chainCommands, deleteSelection, joinTextblockBackward, selectNodeBackward } from "@jvs-milkdown/prose/commands";
|
|
7
|
+
import { DOMParser, Node, Schema } from "@jvs-milkdown/prose/model";
|
|
8
|
+
import { customInputRules } from "@jvs-milkdown/prose";
|
|
9
|
+
import { keymap as keymap$1 } from "@jvs-milkdown/prose/keymap";
|
|
10
|
+
import { EditorState, Plugin, PluginKey } from "@jvs-milkdown/prose/state";
|
|
11
|
+
import { undoInputRule } from "@jvs-milkdown/prose/inputrules";
|
|
12
|
+
import { ParserState, SerializerState } from "@jvs-milkdown/transformer";
|
|
13
|
+
import { EditorView } from "@jvs-milkdown/prose/view";
|
|
14
|
+
//#region src/__internal__/utils.ts
|
|
15
|
+
function withMeta(plugin, meta) {
|
|
16
|
+
plugin.meta = {
|
|
17
|
+
package: "@jvs-milkdown/core",
|
|
18
|
+
group: "System",
|
|
19
|
+
...meta
|
|
20
|
+
};
|
|
21
|
+
return plugin;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/__internal__/remark-handlers.ts
|
|
25
|
+
var remarkHandlers = {
|
|
26
|
+
text: (node, _, state, info) => {
|
|
27
|
+
const value = node.value;
|
|
28
|
+
if (/^[^*_\\]*\s+$/.test(value)) return value;
|
|
29
|
+
return state.safe(value, {
|
|
30
|
+
...info,
|
|
31
|
+
encode: []
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
strong: (node, _, state, info) => {
|
|
35
|
+
const marker = node.marker || state.options.strong || "*";
|
|
36
|
+
const exit = state.enter("strong");
|
|
37
|
+
const tracker = state.createTracker(info);
|
|
38
|
+
let value = tracker.move(marker + marker);
|
|
39
|
+
value += tracker.move(state.containerPhrasing(node, {
|
|
40
|
+
before: value,
|
|
41
|
+
after: marker,
|
|
42
|
+
...tracker.current()
|
|
43
|
+
}));
|
|
44
|
+
value += tracker.move(marker + marker);
|
|
45
|
+
exit();
|
|
46
|
+
return value;
|
|
47
|
+
},
|
|
48
|
+
emphasis: (node, _, state, info) => {
|
|
49
|
+
const marker = node.marker || state.options.emphasis || "*";
|
|
50
|
+
const exit = state.enter("emphasis");
|
|
51
|
+
const tracker = state.createTracker(info);
|
|
52
|
+
let value = tracker.move(marker);
|
|
53
|
+
value += tracker.move(state.containerPhrasing(node, {
|
|
54
|
+
before: value,
|
|
55
|
+
after: marker,
|
|
56
|
+
...tracker.current()
|
|
57
|
+
}));
|
|
58
|
+
value += tracker.move(marker);
|
|
59
|
+
exit();
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/internal-plugin/atoms.ts
|
|
65
|
+
var editorViewCtx = createSlice({}, "editorView");
|
|
66
|
+
var editorStateCtx = createSlice({}, "editorState");
|
|
67
|
+
var initTimerCtx = createSlice([], "initTimer");
|
|
68
|
+
var editorCtx = createSlice({}, "editor");
|
|
69
|
+
var inputRulesCtx = createSlice([], "inputRules");
|
|
70
|
+
var prosePluginsCtx = createSlice([], "prosePlugins");
|
|
71
|
+
var remarkPluginsCtx = createSlice([], "remarkPlugins");
|
|
72
|
+
var nodeViewCtx = createSlice([], "nodeView");
|
|
73
|
+
var markViewCtx = createSlice([], "markView");
|
|
74
|
+
var remarkCtx = createSlice(unified().use(remarkParse).use(remarkStringify), "remark");
|
|
75
|
+
var remarkStringifyOptionsCtx = createSlice({
|
|
76
|
+
handlers: remarkHandlers,
|
|
77
|
+
encode: []
|
|
78
|
+
}, "remarkStringifyOptions");
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/internal-plugin/config.ts
|
|
81
|
+
var ConfigReady = createTimer("ConfigReady");
|
|
82
|
+
function config(configure) {
|
|
83
|
+
const plugin = (ctx) => {
|
|
84
|
+
ctx.record(ConfigReady);
|
|
85
|
+
return async () => {
|
|
86
|
+
await configure(ctx);
|
|
87
|
+
ctx.done(ConfigReady);
|
|
88
|
+
return () => {
|
|
89
|
+
ctx.clearTimer(ConfigReady);
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
withMeta(plugin, { displayName: "Config" });
|
|
94
|
+
return plugin;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/internal-plugin/init.ts
|
|
98
|
+
var InitReady = createTimer("InitReady");
|
|
99
|
+
function init(editor) {
|
|
100
|
+
const plugin = (ctx) => {
|
|
101
|
+
ctx.inject(editorCtx, editor).inject(prosePluginsCtx, []).inject(remarkPluginsCtx, []).inject(inputRulesCtx, []).inject(nodeViewCtx, []).inject(markViewCtx, []).inject(remarkStringifyOptionsCtx, {
|
|
102
|
+
handlers: remarkHandlers,
|
|
103
|
+
encode: []
|
|
104
|
+
}).inject(remarkCtx, unified().use(remarkParse).use(remarkStringify)).inject(initTimerCtx, [ConfigReady]).record(InitReady);
|
|
105
|
+
return async () => {
|
|
106
|
+
await ctx.waitTimers(initTimerCtx);
|
|
107
|
+
const options = ctx.get(remarkStringifyOptionsCtx);
|
|
108
|
+
ctx.set(remarkCtx, unified().use(remarkParse).use(remarkStringify, options));
|
|
109
|
+
ctx.done(InitReady);
|
|
110
|
+
return () => {
|
|
111
|
+
ctx.remove(editorCtx).remove(prosePluginsCtx).remove(remarkPluginsCtx).remove(inputRulesCtx).remove(nodeViewCtx).remove(markViewCtx).remove(remarkStringifyOptionsCtx).remove(remarkCtx).remove(initTimerCtx).clearTimer(InitReady);
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
withMeta(plugin, { displayName: "Init" });
|
|
116
|
+
return plugin;
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/internal-plugin/schema.ts
|
|
120
|
+
var SchemaReady = createTimer("SchemaReady");
|
|
121
|
+
var schemaTimerCtx = createSlice([], "schemaTimer");
|
|
122
|
+
var schemaCtx = createSlice({}, "schema");
|
|
123
|
+
var nodesCtx = createSlice([], "nodes");
|
|
124
|
+
var marksCtx = createSlice([], "marks");
|
|
125
|
+
function extendPriority(x) {
|
|
126
|
+
return {
|
|
127
|
+
...x,
|
|
128
|
+
parseDOM: x.parseDOM?.map((rule) => ({
|
|
129
|
+
priority: x.priority,
|
|
130
|
+
...rule
|
|
131
|
+
}))
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
var schema = (ctx) => {
|
|
135
|
+
ctx.inject(schemaCtx, {}).inject(nodesCtx, []).inject(marksCtx, []).inject(schemaTimerCtx, [InitReady]).record(SchemaReady);
|
|
136
|
+
return async () => {
|
|
137
|
+
await ctx.waitTimers(schemaTimerCtx);
|
|
138
|
+
const remark = ctx.get(remarkCtx);
|
|
139
|
+
const processor = ctx.get(remarkPluginsCtx).reduce((acc, plug) => acc.use(plug.plugin, plug.options), remark);
|
|
140
|
+
ctx.set(remarkCtx, processor);
|
|
141
|
+
const schema = new Schema({
|
|
142
|
+
nodes: Object.fromEntries(ctx.get(nodesCtx).map(([key, x]) => [key, extendPriority(x)])),
|
|
143
|
+
marks: Object.fromEntries(ctx.get(marksCtx).map(([key, x]) => [key, extendPriority(x)]))
|
|
144
|
+
});
|
|
145
|
+
ctx.set(schemaCtx, schema);
|
|
146
|
+
ctx.done(SchemaReady);
|
|
147
|
+
return () => {
|
|
148
|
+
ctx.remove(schemaCtx).remove(nodesCtx).remove(marksCtx).remove(schemaTimerCtx).clearTimer(SchemaReady);
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
withMeta(schema, { displayName: "Schema" });
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/internal-plugin/commands.ts
|
|
155
|
+
var CommandManager = class {
|
|
156
|
+
constructor() {
|
|
157
|
+
this.setCtx = (ctx) => {
|
|
158
|
+
this.#ctx = ctx;
|
|
159
|
+
};
|
|
160
|
+
this.chain = () => {
|
|
161
|
+
if (this.#ctx == null) throw callCommandBeforeEditorView();
|
|
162
|
+
const ctx = this.#ctx;
|
|
163
|
+
const commands = [];
|
|
164
|
+
const get = this.get.bind(this);
|
|
165
|
+
const chains = {
|
|
166
|
+
run: () => {
|
|
167
|
+
const chained = chainCommands(...commands);
|
|
168
|
+
const view = ctx.get(editorViewCtx);
|
|
169
|
+
return chained(view.state, view.dispatch, view);
|
|
170
|
+
},
|
|
171
|
+
inline: (command) => {
|
|
172
|
+
commands.push(command);
|
|
173
|
+
return chains;
|
|
174
|
+
},
|
|
175
|
+
pipe: pipe.bind(this)
|
|
176
|
+
};
|
|
177
|
+
function pipe(slice, payload) {
|
|
178
|
+
const cmd = get(slice);
|
|
179
|
+
commands.push(cmd(payload));
|
|
180
|
+
return chains;
|
|
181
|
+
}
|
|
182
|
+
return chains;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
#container = new Container();
|
|
186
|
+
#ctx = null;
|
|
187
|
+
get ctx() {
|
|
188
|
+
return this.#ctx;
|
|
189
|
+
}
|
|
190
|
+
create(meta, value) {
|
|
191
|
+
const slice = meta.create(this.#container.sliceMap);
|
|
192
|
+
slice.set(value);
|
|
193
|
+
return slice;
|
|
194
|
+
}
|
|
195
|
+
get(slice) {
|
|
196
|
+
return this.#container.get(slice).get();
|
|
197
|
+
}
|
|
198
|
+
remove(slice) {
|
|
199
|
+
return this.#container.remove(slice);
|
|
200
|
+
}
|
|
201
|
+
call(slice, payload) {
|
|
202
|
+
if (this.#ctx == null) throw callCommandBeforeEditorView();
|
|
203
|
+
const command = this.get(slice)(payload);
|
|
204
|
+
const view = this.#ctx.get(editorViewCtx);
|
|
205
|
+
return command(view.state, view.dispatch, view);
|
|
206
|
+
}
|
|
207
|
+
inline(command) {
|
|
208
|
+
if (this.#ctx == null) throw callCommandBeforeEditorView();
|
|
209
|
+
const view = this.#ctx.get(editorViewCtx);
|
|
210
|
+
return command(view.state, view.dispatch, view);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function createCmdKey(key = "cmdKey") {
|
|
214
|
+
return createSlice((() => () => false), key);
|
|
215
|
+
}
|
|
216
|
+
var commandsCtx = createSlice(new CommandManager(), "commands");
|
|
217
|
+
var commandsTimerCtx = createSlice([SchemaReady], "commandsTimer");
|
|
218
|
+
var CommandsReady = createTimer("CommandsReady");
|
|
219
|
+
var commands = (ctx) => {
|
|
220
|
+
const cmd = new CommandManager();
|
|
221
|
+
cmd.setCtx(ctx);
|
|
222
|
+
ctx.inject(commandsCtx, cmd).inject(commandsTimerCtx, [SchemaReady]).record(CommandsReady);
|
|
223
|
+
return async () => {
|
|
224
|
+
await ctx.waitTimers(commandsTimerCtx);
|
|
225
|
+
ctx.done(CommandsReady);
|
|
226
|
+
return () => {
|
|
227
|
+
ctx.remove(commandsCtx).remove(commandsTimerCtx).clearTimer(CommandsReady);
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
withMeta(commands, { displayName: "Commands" });
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/internal-plugin/keymap.ts
|
|
234
|
+
function overrideBaseKeymap(keymap) {
|
|
235
|
+
keymap.Backspace = chainCommands(undoInputRule, deleteSelection, joinTextblockBackward, selectNodeBackward);
|
|
236
|
+
return keymap;
|
|
237
|
+
}
|
|
238
|
+
var KeymapManager = class {
|
|
239
|
+
constructor() {
|
|
240
|
+
this.setCtx = (ctx) => {
|
|
241
|
+
this.#ctx = ctx;
|
|
242
|
+
};
|
|
243
|
+
this.add = (keymap) => {
|
|
244
|
+
this.#keymap.push(keymap);
|
|
245
|
+
return () => {
|
|
246
|
+
this.#keymap = this.#keymap.filter((item) => item !== keymap);
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
this.addObjectKeymap = (keymaps) => {
|
|
250
|
+
const remove = [];
|
|
251
|
+
Object.entries(keymaps).forEach(([key, command]) => {
|
|
252
|
+
if (typeof command === "function") {
|
|
253
|
+
const keymapItem = {
|
|
254
|
+
key,
|
|
255
|
+
onRun: () => command
|
|
256
|
+
};
|
|
257
|
+
this.#keymap.push(keymapItem);
|
|
258
|
+
remove.push(() => {
|
|
259
|
+
this.#keymap = this.#keymap.filter((item) => item !== keymapItem);
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
this.#keymap.push(command);
|
|
263
|
+
remove.push(() => {
|
|
264
|
+
this.#keymap = this.#keymap.filter((item) => item !== command);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return () => {
|
|
269
|
+
remove.forEach((fn) => fn());
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
this.addBaseKeymap = () => {
|
|
273
|
+
const base = overrideBaseKeymap(baseKeymap);
|
|
274
|
+
return this.addObjectKeymap(base);
|
|
275
|
+
};
|
|
276
|
+
this.build = () => {
|
|
277
|
+
const keymap = {};
|
|
278
|
+
this.#keymap.forEach((item) => {
|
|
279
|
+
keymap[item.key] = [...keymap[item.key] || [], item];
|
|
280
|
+
});
|
|
281
|
+
return Object.fromEntries(Object.entries(keymap).map(([key, items]) => {
|
|
282
|
+
const sortedItems = items.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50));
|
|
283
|
+
const command = (state, dispatch, view) => {
|
|
284
|
+
const ctx = this.#ctx;
|
|
285
|
+
if (ctx == null) throw ctxCallOutOfScope();
|
|
286
|
+
return chainCommands(...sortedItems.map((item) => item.onRun(ctx)))(state, dispatch, view);
|
|
287
|
+
};
|
|
288
|
+
return [key, command];
|
|
289
|
+
}));
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
#ctx = null;
|
|
293
|
+
#keymap = [];
|
|
294
|
+
get ctx() {
|
|
295
|
+
return this.#ctx;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var keymapCtx = createSlice(new KeymapManager(), "keymap");
|
|
299
|
+
var keymapTimerCtx = createSlice([SchemaReady], "keymapTimer");
|
|
300
|
+
var KeymapReady = createTimer("KeymapReady");
|
|
301
|
+
var keymap = (ctx) => {
|
|
302
|
+
const km = new KeymapManager();
|
|
303
|
+
km.setCtx(ctx);
|
|
304
|
+
ctx.inject(keymapCtx, km).inject(keymapTimerCtx, [SchemaReady]).record(KeymapReady);
|
|
305
|
+
return async () => {
|
|
306
|
+
await ctx.waitTimers(keymapTimerCtx);
|
|
307
|
+
ctx.done(KeymapReady);
|
|
308
|
+
return () => {
|
|
309
|
+
ctx.remove(keymapCtx).remove(keymapTimerCtx).clearTimer(KeymapReady);
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region src/internal-plugin/parser.ts
|
|
315
|
+
var ParserReady = createTimer("ParserReady");
|
|
316
|
+
var outOfScope$1 = (() => {
|
|
317
|
+
throw ctxCallOutOfScope();
|
|
318
|
+
});
|
|
319
|
+
var parserCtx = createSlice(outOfScope$1, "parser");
|
|
320
|
+
var parserTimerCtx = createSlice([], "parserTimer");
|
|
321
|
+
var parser = (ctx) => {
|
|
322
|
+
ctx.inject(parserCtx, outOfScope$1).inject(parserTimerCtx, [SchemaReady]).record(ParserReady);
|
|
323
|
+
return async () => {
|
|
324
|
+
await ctx.waitTimers(parserTimerCtx);
|
|
325
|
+
const remark = ctx.get(remarkCtx);
|
|
326
|
+
const schema = ctx.get(schemaCtx);
|
|
327
|
+
ctx.set(parserCtx, ParserState.create(schema, remark));
|
|
328
|
+
ctx.done(ParserReady);
|
|
329
|
+
return () => {
|
|
330
|
+
ctx.remove(parserCtx).remove(parserTimerCtx).clearTimer(ParserReady);
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
withMeta(parser, { displayName: "Parser" });
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/internal-plugin/serializer.ts
|
|
337
|
+
var SerializerReady = createTimer("SerializerReady");
|
|
338
|
+
var serializerTimerCtx = createSlice([], "serializerTimer");
|
|
339
|
+
var outOfScope = (() => {
|
|
340
|
+
throw ctxCallOutOfScope();
|
|
341
|
+
});
|
|
342
|
+
var serializerCtx = createSlice(outOfScope, "serializer");
|
|
343
|
+
var serializer = (ctx) => {
|
|
344
|
+
ctx.inject(serializerCtx, outOfScope).inject(serializerTimerCtx, [SchemaReady]).record(SerializerReady);
|
|
345
|
+
return async () => {
|
|
346
|
+
await ctx.waitTimers(serializerTimerCtx);
|
|
347
|
+
const remark = ctx.get(remarkCtx);
|
|
348
|
+
const schema = ctx.get(schemaCtx);
|
|
349
|
+
ctx.set(serializerCtx, SerializerState.create(schema, remark));
|
|
350
|
+
ctx.done(SerializerReady);
|
|
351
|
+
return () => {
|
|
352
|
+
ctx.remove(serializerCtx).remove(serializerTimerCtx).clearTimer(SerializerReady);
|
|
353
|
+
};
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
withMeta(serializer, { displayName: "Serializer" });
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/internal-plugin/editor-state.ts
|
|
359
|
+
var defaultValueCtx = createSlice("", "defaultValue");
|
|
360
|
+
var editorStateOptionsCtx = createSlice((x) => x, "stateOptions");
|
|
361
|
+
var editorStateTimerCtx = createSlice([], "editorStateTimer");
|
|
362
|
+
var EditorStateReady = createTimer("EditorStateReady");
|
|
363
|
+
function getDoc(defaultValue, parser, schema) {
|
|
364
|
+
if (typeof defaultValue === "string") return parser(defaultValue);
|
|
365
|
+
if (defaultValue.type === "html") return DOMParser.fromSchema(schema).parse(defaultValue.dom);
|
|
366
|
+
if (defaultValue.type === "json") return Node.fromJSON(schema, defaultValue.value);
|
|
367
|
+
throw docTypeError(defaultValue);
|
|
368
|
+
}
|
|
369
|
+
var key$1 = new PluginKey("MILKDOWN_STATE_TRACKER");
|
|
370
|
+
var editorState = (ctx) => {
|
|
371
|
+
ctx.inject(defaultValueCtx, "").inject(editorStateCtx, {}).inject(editorStateOptionsCtx, (x) => x).inject(editorStateTimerCtx, [
|
|
372
|
+
ParserReady,
|
|
373
|
+
SerializerReady,
|
|
374
|
+
CommandsReady,
|
|
375
|
+
KeymapReady
|
|
376
|
+
]).record(EditorStateReady);
|
|
377
|
+
return async () => {
|
|
378
|
+
await ctx.waitTimers(editorStateTimerCtx);
|
|
379
|
+
const schema = ctx.get(schemaCtx);
|
|
380
|
+
const parser = ctx.get(parserCtx);
|
|
381
|
+
const rules = ctx.get(inputRulesCtx);
|
|
382
|
+
const optionsOverride = ctx.get(editorStateOptionsCtx);
|
|
383
|
+
const prosePlugins = ctx.get(prosePluginsCtx);
|
|
384
|
+
const doc = getDoc(ctx.get(defaultValueCtx), parser, schema);
|
|
385
|
+
const km = ctx.get(keymapCtx);
|
|
386
|
+
const disposeBaseKeymap = km.addBaseKeymap();
|
|
387
|
+
const plugins = [
|
|
388
|
+
...prosePlugins,
|
|
389
|
+
new Plugin({
|
|
390
|
+
key: key$1,
|
|
391
|
+
state: {
|
|
392
|
+
init: () => {},
|
|
393
|
+
apply: (_tr, _value, _oldState, newState) => {
|
|
394
|
+
ctx.set(editorStateCtx, newState);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}),
|
|
398
|
+
customInputRules({ rules }),
|
|
399
|
+
keymap$1(km.build())
|
|
400
|
+
];
|
|
401
|
+
ctx.set(prosePluginsCtx, plugins);
|
|
402
|
+
const options = optionsOverride({
|
|
403
|
+
schema,
|
|
404
|
+
doc,
|
|
405
|
+
plugins
|
|
406
|
+
});
|
|
407
|
+
const state = EditorState.create(options);
|
|
408
|
+
ctx.set(editorStateCtx, state);
|
|
409
|
+
ctx.done(EditorStateReady);
|
|
410
|
+
return () => {
|
|
411
|
+
disposeBaseKeymap();
|
|
412
|
+
ctx.remove(defaultValueCtx).remove(editorStateCtx).remove(editorStateOptionsCtx).remove(editorStateTimerCtx).clearTimer(EditorStateReady);
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
withMeta(editorState, { displayName: "EditorState" });
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/internal-plugin/paste-rule.ts
|
|
419
|
+
var pasteRulesCtx = createSlice([], "pasteRule");
|
|
420
|
+
var pasteRulesTimerCtx = createSlice([SchemaReady], "pasteRuleTimer");
|
|
421
|
+
var PasteRulesReady = createTimer("PasteRuleReady");
|
|
422
|
+
var pasteRule = (ctx) => {
|
|
423
|
+
ctx.inject(pasteRulesCtx, []).inject(pasteRulesTimerCtx, [SchemaReady]).record(PasteRulesReady);
|
|
424
|
+
return async () => {
|
|
425
|
+
await ctx.waitTimers(pasteRulesTimerCtx);
|
|
426
|
+
ctx.done(PasteRulesReady);
|
|
427
|
+
return () => {
|
|
428
|
+
ctx.remove(pasteRulesCtx).remove(pasteRulesTimerCtx).clearTimer(PasteRulesReady);
|
|
429
|
+
};
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
withMeta(pasteRule, { displayName: "PasteRule" });
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/internal-plugin/editor-view.ts
|
|
435
|
+
var EditorViewReady = createTimer("EditorViewReady");
|
|
436
|
+
var editorViewTimerCtx = createSlice([], "editorViewTimer");
|
|
437
|
+
var editorViewOptionsCtx = createSlice({}, "editorViewOptions");
|
|
438
|
+
var rootCtx = createSlice(null, "root");
|
|
439
|
+
var rootDOMCtx = createSlice(null, "rootDOM");
|
|
440
|
+
var rootAttrsCtx = createSlice({}, "rootAttrs");
|
|
441
|
+
function createViewContainer(root, ctx) {
|
|
442
|
+
const container = document.createElement("div");
|
|
443
|
+
container.className = "milkdown";
|
|
444
|
+
root.appendChild(container);
|
|
445
|
+
ctx.set(rootDOMCtx, container);
|
|
446
|
+
const attrs = ctx.get(rootAttrsCtx);
|
|
447
|
+
Object.entries(attrs).forEach(([key, value]) => container.setAttribute(key, value));
|
|
448
|
+
return container;
|
|
449
|
+
}
|
|
450
|
+
function prepareViewDom(dom) {
|
|
451
|
+
dom.classList.add("editor");
|
|
452
|
+
dom.setAttribute("role", "textbox");
|
|
453
|
+
}
|
|
454
|
+
var key = new PluginKey("MILKDOWN_VIEW_CLEAR");
|
|
455
|
+
var editorView = (ctx) => {
|
|
456
|
+
ctx.inject(rootCtx, document.body).inject(editorViewCtx, {}).inject(editorViewOptionsCtx, {}).inject(rootDOMCtx, null).inject(rootAttrsCtx, {}).inject(editorViewTimerCtx, [EditorStateReady, PasteRulesReady]).record(EditorViewReady);
|
|
457
|
+
return async () => {
|
|
458
|
+
await ctx.wait(InitReady);
|
|
459
|
+
const root = ctx.get(rootCtx) || document.body;
|
|
460
|
+
const el = typeof root === "string" ? document.querySelector(root) : root;
|
|
461
|
+
ctx.update(prosePluginsCtx, (xs) => [new Plugin({
|
|
462
|
+
key,
|
|
463
|
+
view: (editorView) => {
|
|
464
|
+
const container = el ? createViewContainer(el, ctx) : void 0;
|
|
465
|
+
const handleDOM = () => {
|
|
466
|
+
if (container && el) {
|
|
467
|
+
const editor = editorView.dom;
|
|
468
|
+
el.replaceChild(container, editor);
|
|
469
|
+
container.appendChild(editor);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
handleDOM();
|
|
473
|
+
return { destroy: () => {
|
|
474
|
+
if (container?.parentNode) container?.parentNode.replaceChild(editorView.dom, container);
|
|
475
|
+
container?.remove();
|
|
476
|
+
} };
|
|
477
|
+
}
|
|
478
|
+
}), ...xs]);
|
|
479
|
+
await ctx.waitTimers(editorViewTimerCtx);
|
|
480
|
+
const state = ctx.get(editorStateCtx);
|
|
481
|
+
const options = ctx.get(editorViewOptionsCtx);
|
|
482
|
+
const view = new EditorView(el, {
|
|
483
|
+
state,
|
|
484
|
+
nodeViews: Object.fromEntries(ctx.get(nodeViewCtx)),
|
|
485
|
+
markViews: Object.fromEntries(ctx.get(markViewCtx)),
|
|
486
|
+
transformPasted: (slice, view, isPlainText) => {
|
|
487
|
+
ctx.get(pasteRulesCtx).sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50)).map((rule) => rule.run).forEach((runner) => {
|
|
488
|
+
slice = runner(slice, view, isPlainText);
|
|
489
|
+
});
|
|
490
|
+
return slice;
|
|
491
|
+
},
|
|
492
|
+
...options
|
|
493
|
+
});
|
|
494
|
+
prepareViewDom(view.dom);
|
|
495
|
+
ctx.set(editorViewCtx, view);
|
|
496
|
+
ctx.done(EditorViewReady);
|
|
497
|
+
return () => {
|
|
498
|
+
view?.destroy();
|
|
499
|
+
ctx.remove(rootCtx).remove(editorViewCtx).remove(editorViewOptionsCtx).remove(rootDOMCtx).remove(rootAttrsCtx).remove(editorViewTimerCtx).clearTimer(EditorViewReady);
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
withMeta(editorView, { displayName: "EditorView" });
|
|
504
|
+
//#endregion
|
|
505
|
+
//#region src/editor/editor.ts
|
|
506
|
+
var EditorStatus = /* @__PURE__ */ function(EditorStatus) {
|
|
507
|
+
EditorStatus["Idle"] = "Idle";
|
|
508
|
+
EditorStatus["OnCreate"] = "OnCreate";
|
|
509
|
+
EditorStatus["Created"] = "Created";
|
|
510
|
+
EditorStatus["OnDestroy"] = "OnDestroy";
|
|
511
|
+
EditorStatus["Destroyed"] = "Destroyed";
|
|
512
|
+
return EditorStatus;
|
|
513
|
+
}({});
|
|
514
|
+
var Editor = class Editor {
|
|
515
|
+
constructor() {
|
|
516
|
+
this.enableInspector = (enable = true) => {
|
|
517
|
+
this.#enableInspector = enable;
|
|
518
|
+
return this;
|
|
519
|
+
};
|
|
520
|
+
this.onStatusChange = (onChange) => {
|
|
521
|
+
this.#onStatusChange = onChange;
|
|
522
|
+
return this;
|
|
523
|
+
};
|
|
524
|
+
this.config = (configure) => {
|
|
525
|
+
this.#configureList.push(configure);
|
|
526
|
+
return this;
|
|
527
|
+
};
|
|
528
|
+
this.removeConfig = (configure) => {
|
|
529
|
+
this.#configureList = this.#configureList.filter((x) => x !== configure);
|
|
530
|
+
return this;
|
|
531
|
+
};
|
|
532
|
+
this.use = (plugins) => {
|
|
533
|
+
const _plugins = [plugins].flat();
|
|
534
|
+
_plugins.flat().forEach((plugin) => {
|
|
535
|
+
this.#usrPluginStore.set(plugin, {
|
|
536
|
+
ctx: void 0,
|
|
537
|
+
handler: void 0,
|
|
538
|
+
cleanup: void 0
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
if (this.#status === EditorStatus.Created) this.#prepare(_plugins, this.#usrPluginStore);
|
|
542
|
+
return this;
|
|
543
|
+
};
|
|
544
|
+
this.remove = async (plugins) => {
|
|
545
|
+
if (this.#status === EditorStatus.OnCreate) {
|
|
546
|
+
console.warn("[Milkdown]: You are trying to remove plugins when the editor is creating, this is not recommended, please check your code.");
|
|
547
|
+
return new Promise((resolve) => {
|
|
548
|
+
setTimeout(() => {
|
|
549
|
+
resolve(this.remove(plugins));
|
|
550
|
+
}, 50);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
await this.#cleanup([plugins].flat(), true);
|
|
554
|
+
return this;
|
|
555
|
+
};
|
|
556
|
+
this.create = async () => {
|
|
557
|
+
if (this.#status === EditorStatus.OnCreate) return this;
|
|
558
|
+
if (this.#status === EditorStatus.Created) await this.destroy();
|
|
559
|
+
this.#setStatus(EditorStatus.OnCreate);
|
|
560
|
+
this.#loadInternal();
|
|
561
|
+
this.#prepare([...this.#usrPluginStore.keys()], this.#usrPluginStore);
|
|
562
|
+
await Promise.all([this.#loadPluginInStore(this.#sysPluginStore), this.#loadPluginInStore(this.#usrPluginStore)].flat());
|
|
563
|
+
this.#setStatus(EditorStatus.Created);
|
|
564
|
+
return this;
|
|
565
|
+
};
|
|
566
|
+
this.destroy = async (clearPlugins = false) => {
|
|
567
|
+
if (this.#status === EditorStatus.Destroyed || this.#status === EditorStatus.OnDestroy) return this;
|
|
568
|
+
if (this.#status === EditorStatus.OnCreate) return new Promise((resolve) => {
|
|
569
|
+
setTimeout(() => {
|
|
570
|
+
resolve(this.destroy(clearPlugins));
|
|
571
|
+
}, 50);
|
|
572
|
+
});
|
|
573
|
+
if (clearPlugins) this.#configureList = [];
|
|
574
|
+
this.#setStatus(EditorStatus.OnDestroy);
|
|
575
|
+
await this.#cleanup([...this.#usrPluginStore.keys()], clearPlugins);
|
|
576
|
+
await this.#cleanupInternal();
|
|
577
|
+
this.#setStatus(EditorStatus.Destroyed);
|
|
578
|
+
return this;
|
|
579
|
+
};
|
|
580
|
+
this.action = (action) => action(this.#ctx);
|
|
581
|
+
this.inspect = () => {
|
|
582
|
+
if (!this.#enableInspector) {
|
|
583
|
+
console.warn("[Milkdown]: You are trying to collect inspection when inspector is disabled, please enable inspector by `editor.enableInspector()` first.");
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
return [...this.#sysPluginStore.values(), ...this.#usrPluginStore.values()].map(({ ctx }) => ctx?.inspector?.read()).filter((x) => Boolean(x));
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
static make() {
|
|
590
|
+
return new Editor();
|
|
591
|
+
}
|
|
592
|
+
#enableInspector = false;
|
|
593
|
+
#status = EditorStatus.Idle;
|
|
594
|
+
#configureList = [];
|
|
595
|
+
#onStatusChange = () => void 0;
|
|
596
|
+
#container = new Container();
|
|
597
|
+
#clock = new Clock();
|
|
598
|
+
#usrPluginStore = /* @__PURE__ */ new Map();
|
|
599
|
+
#sysPluginStore = /* @__PURE__ */ new Map();
|
|
600
|
+
#ctx = new Ctx(this.#container, this.#clock);
|
|
601
|
+
#loadInternal = () => {
|
|
602
|
+
const configPlugin = config(async (ctx) => {
|
|
603
|
+
await Promise.all(this.#configureList.map((fn) => Promise.resolve(fn(ctx))));
|
|
604
|
+
});
|
|
605
|
+
const internalPlugins = [
|
|
606
|
+
schema,
|
|
607
|
+
parser,
|
|
608
|
+
serializer,
|
|
609
|
+
commands,
|
|
610
|
+
keymap,
|
|
611
|
+
pasteRule,
|
|
612
|
+
editorState,
|
|
613
|
+
editorView,
|
|
614
|
+
init(this),
|
|
615
|
+
configPlugin
|
|
616
|
+
];
|
|
617
|
+
this.#prepare(internalPlugins, this.#sysPluginStore);
|
|
618
|
+
};
|
|
619
|
+
#prepare = (plugins, store) => {
|
|
620
|
+
plugins.forEach((plugin) => {
|
|
621
|
+
const ctx = this.#ctx.produce(this.#enableInspector ? plugin.meta : void 0);
|
|
622
|
+
const handler = plugin(ctx);
|
|
623
|
+
store.set(plugin, {
|
|
624
|
+
ctx,
|
|
625
|
+
handler,
|
|
626
|
+
cleanup: void 0
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
#cleanup = (plugins, remove = false) => {
|
|
631
|
+
return Promise.all([plugins].flat().map(async (plugin) => {
|
|
632
|
+
const cleanup = this.#usrPluginStore.get(plugin)?.cleanup;
|
|
633
|
+
if (remove) this.#usrPluginStore.delete(plugin);
|
|
634
|
+
else this.#usrPluginStore.set(plugin, {
|
|
635
|
+
ctx: void 0,
|
|
636
|
+
handler: void 0,
|
|
637
|
+
cleanup: void 0
|
|
638
|
+
});
|
|
639
|
+
if (typeof cleanup === "function") return cleanup();
|
|
640
|
+
return cleanup;
|
|
641
|
+
}));
|
|
642
|
+
};
|
|
643
|
+
#cleanupInternal = async () => {
|
|
644
|
+
await Promise.all([...this.#sysPluginStore.entries()].map(async ([_, { cleanup }]) => {
|
|
645
|
+
if (typeof cleanup === "function") return cleanup();
|
|
646
|
+
return cleanup;
|
|
647
|
+
}));
|
|
648
|
+
this.#sysPluginStore.clear();
|
|
649
|
+
};
|
|
650
|
+
#setStatus = (status) => {
|
|
651
|
+
this.#status = status;
|
|
652
|
+
this.#onStatusChange(status);
|
|
653
|
+
};
|
|
654
|
+
#loadPluginInStore = (store) => {
|
|
655
|
+
return [...store.entries()].map(async ([key, loader]) => {
|
|
656
|
+
const { ctx, handler } = loader;
|
|
657
|
+
if (!handler) return;
|
|
658
|
+
const cleanup = await handler();
|
|
659
|
+
store.set(key, {
|
|
660
|
+
ctx,
|
|
661
|
+
handler,
|
|
662
|
+
cleanup
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
};
|
|
666
|
+
get ctx() {
|
|
667
|
+
return this.#ctx;
|
|
668
|
+
}
|
|
669
|
+
get status() {
|
|
670
|
+
return this.#status;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
//#endregion
|
|
674
|
+
export { CommandManager, CommandsReady, ConfigReady, Editor, EditorStateReady, EditorStatus, EditorViewReady, InitReady, KeymapManager, KeymapReady, ParserReady, PasteRulesReady, SchemaReady, SerializerReady, commands, commandsCtx, commandsTimerCtx, config, createCmdKey, defaultValueCtx, editorCtx, editorState, editorStateCtx, editorStateOptionsCtx, editorStateTimerCtx, editorView, editorViewCtx, editorViewOptionsCtx, editorViewTimerCtx, getDoc, init, initTimerCtx, inputRulesCtx, keymap, keymapCtx, keymapTimerCtx, markViewCtx, marksCtx, nodeViewCtx, nodesCtx, parser, parserCtx, parserTimerCtx, pasteRule, pasteRulesCtx, pasteRulesTimerCtx, prosePluginsCtx, remarkCtx, remarkPluginsCtx, remarkStringifyOptionsCtx, rootAttrsCtx, rootCtx, rootDOMCtx, schema, schemaCtx, schemaTimerCtx, serializer, serializerCtx, serializerTimerCtx };
|
|
675
|
+
|
|
676
|
+
//# sourceMappingURL=index.js.map
|