@jvs-milkdown/utils 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 +9 -0
- package/lib/composable/$command.d.ts +9 -0
- package/lib/composable/$command.d.ts.map +1 -0
- package/lib/composable/$ctx.d.ts +6 -0
- package/lib/composable/$ctx.d.ts.map +1 -0
- package/lib/composable/$input-rule.d.ts +8 -0
- package/lib/composable/$input-rule.d.ts.map +1 -0
- package/lib/composable/$mark.d.ts +11 -0
- package/lib/composable/$mark.d.ts.map +1 -0
- package/lib/composable/$node.d.ts +11 -0
- package/lib/composable/$node.d.ts.map +1 -0
- package/lib/composable/$paste-rule.d.ts +8 -0
- package/lib/composable/$paste-rule.d.ts.map +1 -0
- package/lib/composable/$prose.d.ts +9 -0
- package/lib/composable/$prose.d.ts.map +1 -0
- package/lib/composable/$shortcut.d.ts +10 -0
- package/lib/composable/$shortcut.d.ts.map +1 -0
- package/lib/composable/$view.d.ts +11 -0
- package/lib/composable/$view.d.ts.map +1 -0
- package/lib/composable/composed/$attr.d.ts +7 -0
- package/lib/composable/composed/$attr.d.ts.map +1 -0
- package/lib/composable/composed/$mark-schema.d.ts +18 -0
- package/lib/composable/composed/$mark-schema.d.ts.map +1 -0
- package/lib/composable/composed/$node-schema.d.ts +18 -0
- package/lib/composable/composed/$node-schema.d.ts.map +1 -0
- package/lib/composable/composed/$node-schema.spec.d.ts +2 -0
- package/lib/composable/composed/$node-schema.spec.d.ts.map +1 -0
- package/lib/composable/composed/$remark.d.ts +13 -0
- package/lib/composable/composed/$remark.d.ts.map +1 -0
- package/lib/composable/composed/$use-keymap.d.ts +25 -0
- package/lib/composable/composed/$use-keymap.d.ts.map +1 -0
- package/lib/composable/composed/index.d.ts +6 -0
- package/lib/composable/composed/index.d.ts.map +1 -0
- package/lib/composable/index.d.ts +12 -0
- package/lib/composable/index.d.ts.map +1 -0
- package/lib/composable/utils.d.ts +7 -0
- package/lib/composable/utils.d.ts.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +549 -0
- package/lib/index.js.map +1 -0
- package/lib/macro/call-command.d.ts +8 -0
- package/lib/macro/call-command.d.ts.map +1 -0
- package/lib/macro/force-update.d.ts +3 -0
- package/lib/macro/force-update.d.ts.map +1 -0
- package/lib/macro/get-html.d.ts +3 -0
- package/lib/macro/get-html.d.ts.map +1 -0
- package/lib/macro/get-markdown.d.ts +6 -0
- package/lib/macro/get-markdown.d.ts.map +1 -0
- package/lib/macro/index.d.ts +12 -0
- package/lib/macro/index.d.ts.map +1 -0
- package/lib/macro/insert-pos.d.ts +3 -0
- package/lib/macro/insert-pos.d.ts.map +1 -0
- package/lib/macro/insert.d.ts +3 -0
- package/lib/macro/insert.d.ts.map +1 -0
- package/lib/macro/markdown-to-slice.d.ts +3 -0
- package/lib/macro/markdown-to-slice.d.ts.map +1 -0
- package/lib/macro/outline.d.ts +7 -0
- package/lib/macro/outline.d.ts.map +1 -0
- package/lib/macro/replace-all.d.ts +3 -0
- package/lib/macro/replace-all.d.ts.map +1 -0
- package/lib/macro/replace-range.d.ts +6 -0
- package/lib/macro/replace-range.d.ts.map +1 -0
- package/lib/macro/set-attr.d.ts +4 -0
- package/lib/macro/set-attr.d.ts.map +1 -0
- package/lib/pipe.d.ts +7 -0
- package/lib/pipe.d.ts.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +35 -0
- package/src/composable/$command.ts +102 -0
- package/src/composable/$ctx.ts +31 -0
- package/src/composable/$input-rule.ts +55 -0
- package/src/composable/$mark.ts +87 -0
- package/src/composable/$node.ts +90 -0
- package/src/composable/$paste-rule.ts +59 -0
- package/src/composable/$prose.ts +70 -0
- package/src/composable/$shortcut.ts +66 -0
- package/src/composable/$view.ts +110 -0
- package/src/composable/composed/$attr.ts +29 -0
- package/src/composable/composed/$mark-schema.ts +65 -0
- package/src/composable/composed/$node-schema.spec.ts +139 -0
- package/src/composable/composed/$node-schema.ts +65 -0
- package/src/composable/composed/$remark.ts +53 -0
- package/src/composable/composed/$use-keymap.ts +91 -0
- package/src/composable/composed/index.ts +5 -0
- package/src/composable/index.ts +12 -0
- package/src/composable/utils.ts +63 -0
- package/src/index.ts +3 -0
- package/src/macro/call-command.ts +28 -0
- package/src/macro/force-update.ts +14 -0
- package/src/macro/get-html.ts +20 -0
- package/src/macro/get-markdown.ts +27 -0
- package/src/macro/index.ts +11 -0
- package/src/macro/insert-pos.ts +27 -0
- package/src/macro/insert.ts +38 -0
- package/src/macro/markdown-to-slice.ts +18 -0
- package/src/macro/outline.ts +21 -0
- package/src/macro/replace-all.ts +46 -0
- package/src/macro/replace-range.ts +18 -0
- package/src/macro/set-attr.ts +16 -0
- package/src/pipe.ts +72 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MilkdownPlugin, SliceType } from '@jvs-milkdown/ctx'
|
|
2
|
+
|
|
3
|
+
import { createSlice } from '@jvs-milkdown/ctx'
|
|
4
|
+
|
|
5
|
+
/// @internal
|
|
6
|
+
export type $Ctx<T, N extends string> = MilkdownPlugin & {
|
|
7
|
+
key: SliceType<T, N>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Create a slice plugin. The plugin will be registered in the `ctx` and can be accessed by other parts of the editor.
|
|
11
|
+
/// ```ts
|
|
12
|
+
/// const counterCtx = $ctx(0, 'counter');
|
|
13
|
+
/// ```
|
|
14
|
+
///
|
|
15
|
+
/// Additional property:
|
|
16
|
+
/// - `key`: The key of the slice.
|
|
17
|
+
export function $ctx<T, N extends string>(value: T, name: N): $Ctx<T, N> {
|
|
18
|
+
const slice = createSlice(value, name)
|
|
19
|
+
const plugin: $Ctx<T, N> = (ctx) => {
|
|
20
|
+
ctx.inject(slice)
|
|
21
|
+
return () => {
|
|
22
|
+
return () => {
|
|
23
|
+
ctx.remove(slice)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
plugin.key = slice
|
|
29
|
+
|
|
30
|
+
return plugin
|
|
31
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { InputRule } from '@jvs-milkdown/prose/inputrules'
|
|
3
|
+
|
|
4
|
+
import { SchemaReady, editorStateTimerCtx, inputRulesCtx } from '@jvs-milkdown/core'
|
|
5
|
+
|
|
6
|
+
import { addTimer } from './utils'
|
|
7
|
+
|
|
8
|
+
/// @internal
|
|
9
|
+
export type $InputRule = MilkdownPlugin & {
|
|
10
|
+
inputRule: InputRule
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// Create an input rule plugin.
|
|
14
|
+
/// It takes a factory function which returns a [prosemirror input rule](https://prosemirror.net/docs/ref/#inputrules.InputRule).
|
|
15
|
+
///
|
|
16
|
+
/// Additional property:
|
|
17
|
+
/// - `inputRule`: The prosemirror input rule created.
|
|
18
|
+
export function $inputRule(inputRule: (ctx: Ctx) => InputRule): $InputRule {
|
|
19
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
20
|
+
await ctx.wait(SchemaReady)
|
|
21
|
+
const ir = inputRule(ctx)
|
|
22
|
+
ctx.update(inputRulesCtx, (irs) => [...irs, ir])
|
|
23
|
+
;(<$InputRule>plugin).inputRule = ir
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
ctx.update(inputRulesCtx, (irs) => irs.filter((x) => x !== ir))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <$InputRule>plugin
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// The async version for `$inputRule`. You can use `await` in the factory when creating the input rule.
|
|
34
|
+
///
|
|
35
|
+
/// Additional property:
|
|
36
|
+
/// - `inputRule`: The prosemirror input rule created.
|
|
37
|
+
/// - `timer`: The timer which will be resolved when the input rule is ready.
|
|
38
|
+
export function $inputRuleAsync(
|
|
39
|
+
inputRule: (ctx: Ctx) => Promise<InputRule>,
|
|
40
|
+
timerName?: string
|
|
41
|
+
) {
|
|
42
|
+
return addTimer<$InputRule>(
|
|
43
|
+
async (ctx, plugin) => {
|
|
44
|
+
await ctx.wait(SchemaReady)
|
|
45
|
+
const ir = await inputRule(ctx)
|
|
46
|
+
ctx.update(inputRulesCtx, (irs) => [...irs, ir])
|
|
47
|
+
plugin.inputRule = ir
|
|
48
|
+
return () => {
|
|
49
|
+
ctx.update(inputRulesCtx, (irs) => irs.filter((x) => x !== ir))
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
editorStateTimerCtx,
|
|
53
|
+
timerName
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { MarkType } from '@jvs-milkdown/prose/model'
|
|
3
|
+
import type { MarkSchema } from '@jvs-milkdown/transformer'
|
|
4
|
+
|
|
5
|
+
import { marksCtx, schemaCtx, schemaTimerCtx } from '@jvs-milkdown/core'
|
|
6
|
+
import { missingMarkInSchema } from '@jvs-milkdown/exception'
|
|
7
|
+
|
|
8
|
+
import { addTimer } from './utils'
|
|
9
|
+
|
|
10
|
+
/// @internal
|
|
11
|
+
export type $Mark = MilkdownPlugin & {
|
|
12
|
+
id: string
|
|
13
|
+
schema: MarkSchema
|
|
14
|
+
type: (ctx: Ctx) => MarkType
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Create a mark plugin.
|
|
18
|
+
/// It takes a mark id and a factory function.
|
|
19
|
+
/// The factory should return a function that returns a [mark schema](/transformer#interface-markschema).
|
|
20
|
+
///
|
|
21
|
+
/// Additional property:
|
|
22
|
+
/// - `id`: The id of the mark.
|
|
23
|
+
/// - `schema`: The mark schema created.
|
|
24
|
+
/// - `type`: A function that will return the [prosemirror mark type](https://prosemirror.net/docs/ref/#model.MarkType).
|
|
25
|
+
export function $mark(id: string, schema: (ctx: Ctx) => MarkSchema): $Mark {
|
|
26
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
27
|
+
const markSchema = schema(ctx)
|
|
28
|
+
ctx.update(marksCtx, (ns) => [
|
|
29
|
+
...ns.filter((n) => n[0] !== id),
|
|
30
|
+
[id, markSchema] as [string, MarkSchema],
|
|
31
|
+
])
|
|
32
|
+
;(<$Mark>plugin).id = id
|
|
33
|
+
;(<$Mark>plugin).schema = markSchema
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
ctx.update(marksCtx, (ns) => ns.filter(([x]) => x !== id))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
;(<$Mark>plugin).type = (ctx) => {
|
|
40
|
+
const markType = ctx.get(schemaCtx).marks[id]
|
|
41
|
+
if (!markType) throw missingMarkInSchema(id)
|
|
42
|
+
return markType
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return <$Mark>plugin
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// The async version for `$mark`. You can use `await` in the factory when creating the mark schema.
|
|
49
|
+
///
|
|
50
|
+
/// Additional property:
|
|
51
|
+
/// - `id`: The id of the mark.
|
|
52
|
+
/// - `schema`: The mark schema created.
|
|
53
|
+
/// - `type`: A function that will return the [prosemirror mark type](https://prosemirror.net/docs/ref/#model.MarkType).
|
|
54
|
+
/// - `timer`: The timer which will be resolved when the mark schema is ready.
|
|
55
|
+
export function $markAsync(
|
|
56
|
+
id: string,
|
|
57
|
+
schema: (ctx: Ctx) => Promise<MarkSchema>,
|
|
58
|
+
timerName?: string
|
|
59
|
+
) {
|
|
60
|
+
const plugin = addTimer<$Mark>(
|
|
61
|
+
async (ctx, plugin, done) => {
|
|
62
|
+
const markSchema = await schema(ctx)
|
|
63
|
+
ctx.update(marksCtx, (ns) => [
|
|
64
|
+
...ns.filter((n) => n[0] !== id),
|
|
65
|
+
[id, markSchema] as [string, MarkSchema],
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
plugin.id = id
|
|
69
|
+
plugin.schema = markSchema
|
|
70
|
+
done()
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
ctx.update(marksCtx, (ns) => ns.filter(([x]) => x !== id))
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
schemaTimerCtx,
|
|
77
|
+
timerName
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
plugin.type = (ctx) => {
|
|
81
|
+
const markType = ctx.get(schemaCtx).marks[id]
|
|
82
|
+
if (!markType) throw missingMarkInSchema(id)
|
|
83
|
+
return markType
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return plugin
|
|
87
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { NodeType } from '@jvs-milkdown/prose/model'
|
|
3
|
+
import type { NodeSchema } from '@jvs-milkdown/transformer'
|
|
4
|
+
|
|
5
|
+
import { nodesCtx, schemaCtx, schemaTimerCtx } from '@jvs-milkdown/core'
|
|
6
|
+
import { missingNodeInSchema } from '@jvs-milkdown/exception'
|
|
7
|
+
|
|
8
|
+
import { addTimer } from './utils'
|
|
9
|
+
|
|
10
|
+
/// @internal
|
|
11
|
+
export type $Node = MilkdownPlugin & {
|
|
12
|
+
id: string
|
|
13
|
+
schema: NodeSchema
|
|
14
|
+
type: (ctx: Ctx) => NodeType
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Create a node plugin.
|
|
18
|
+
/// It takes a node id and a factory function.
|
|
19
|
+
/// The factory should return a function that returns a [node schema](/transformer#interface-nodeschema).
|
|
20
|
+
///
|
|
21
|
+
/// Additional property:
|
|
22
|
+
/// - `id`: The id of the node.
|
|
23
|
+
/// - `schema`: The node schema created.
|
|
24
|
+
/// - `type`: A function that will return the [prosemirror node type](https://prosemirror.net/docs/ref/#model.NodeType).
|
|
25
|
+
export function $node(id: string, schema: (ctx: Ctx) => NodeSchema): $Node {
|
|
26
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
27
|
+
const nodeSchema = schema(ctx)
|
|
28
|
+
ctx.update(nodesCtx, (ns) => [
|
|
29
|
+
...ns.filter((n) => n[0] !== id),
|
|
30
|
+
[id, nodeSchema] as [string, NodeSchema],
|
|
31
|
+
])
|
|
32
|
+
;(<$Node>plugin).id = id
|
|
33
|
+
;(<$Node>plugin).schema = nodeSchema
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
ctx.update(nodesCtx, (ns) => ns.filter(([x]) => x !== id))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
;(<$Node>plugin).type = (ctx) => {
|
|
41
|
+
const nodeType = ctx.get(schemaCtx).nodes[id]
|
|
42
|
+
if (!nodeType) throw missingNodeInSchema(id)
|
|
43
|
+
|
|
44
|
+
return nodeType
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return <$Node>plugin
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// The async version for `$node`. You can use `await` in the factory when creating the node schema.
|
|
51
|
+
///
|
|
52
|
+
/// Additional property:
|
|
53
|
+
/// - `id`: The id of the node.
|
|
54
|
+
/// - `schema`: The node schema created.
|
|
55
|
+
/// - `type`: A function that will return the [prosemirror node type](https://prosemirror.net/docs/ref/#model.NodeType).
|
|
56
|
+
/// - `timer`: The timer which will be resolved when the node schema is ready.
|
|
57
|
+
export function $nodeAsync(
|
|
58
|
+
id: string,
|
|
59
|
+
schema: (ctx: Ctx) => Promise<NodeSchema>,
|
|
60
|
+
timerName?: string
|
|
61
|
+
) {
|
|
62
|
+
const plugin = addTimer<$Node>(
|
|
63
|
+
async (ctx, plugin, done) => {
|
|
64
|
+
const nodeSchema = await schema(ctx)
|
|
65
|
+
ctx.update(nodesCtx, (ns) => [
|
|
66
|
+
...ns.filter((n) => n[0] !== id),
|
|
67
|
+
[id, nodeSchema] as [string, NodeSchema],
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
plugin.id = id
|
|
71
|
+
plugin.schema = nodeSchema
|
|
72
|
+
done()
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
ctx.update(nodesCtx, (ns) => ns.filter(([x]) => x !== id))
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
schemaTimerCtx,
|
|
79
|
+
timerName
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
plugin.type = (ctx) => {
|
|
83
|
+
const nodeType = ctx.get(schemaCtx).nodes[id]
|
|
84
|
+
if (!nodeType) throw missingNodeInSchema(id)
|
|
85
|
+
|
|
86
|
+
return nodeType
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return plugin
|
|
90
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
pasteRulesCtx,
|
|
5
|
+
pasteRulesTimerCtx,
|
|
6
|
+
SchemaReady,
|
|
7
|
+
type PasteRule,
|
|
8
|
+
} from '@jvs-milkdown/core'
|
|
9
|
+
|
|
10
|
+
import { addTimer } from './utils'
|
|
11
|
+
|
|
12
|
+
/// @internal
|
|
13
|
+
export type $PasteRule = MilkdownPlugin & {
|
|
14
|
+
pasteRule: PasteRule
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Create a paste rule plugin.
|
|
18
|
+
/// It takes a factory function which returns a paste rule.
|
|
19
|
+
///
|
|
20
|
+
/// Additional property:
|
|
21
|
+
/// - `pasteRule`: The paste rule created.
|
|
22
|
+
export function $pasteRule(pasteRule: (ctx: Ctx) => PasteRule): $PasteRule {
|
|
23
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
24
|
+
await ctx.wait(SchemaReady)
|
|
25
|
+
const pr = pasteRule(ctx)
|
|
26
|
+
ctx.update(pasteRulesCtx, (prs) => [...prs, pr])
|
|
27
|
+
;(<$PasteRule>plugin).pasteRule = pr
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
ctx.update(pasteRulesCtx, (prs) => prs.filter((x) => x !== pr))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return <$PasteRule>plugin
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// The async version for `$pasteRule`. You can use `await` in the factory when creating the paste rule.
|
|
38
|
+
///
|
|
39
|
+
/// Additional property:
|
|
40
|
+
/// - `pasteRule`: The paste rule created.
|
|
41
|
+
/// - `timer`: The timer which will be resolved when the paste rule is ready.
|
|
42
|
+
export function $pasteRuleAsync(
|
|
43
|
+
pasteRule: (ctx: Ctx) => Promise<PasteRule>,
|
|
44
|
+
timerName?: string
|
|
45
|
+
) {
|
|
46
|
+
return addTimer<$PasteRule>(
|
|
47
|
+
async (ctx, plugin) => {
|
|
48
|
+
await ctx.wait(SchemaReady)
|
|
49
|
+
const pr = await pasteRule(ctx)
|
|
50
|
+
ctx.update(pasteRulesCtx, (prs) => [...prs, pr])
|
|
51
|
+
plugin.pasteRule = pr
|
|
52
|
+
return () => {
|
|
53
|
+
ctx.update(pasteRulesCtx, (prs) => prs.filter((x) => x !== pr))
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
pasteRulesTimerCtx,
|
|
57
|
+
timerName
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { Plugin, PluginKey } from '@jvs-milkdown/prose/state'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SchemaReady,
|
|
6
|
+
editorStateTimerCtx,
|
|
7
|
+
prosePluginsCtx,
|
|
8
|
+
} from '@jvs-milkdown/core'
|
|
9
|
+
|
|
10
|
+
import { addTimer } from './utils'
|
|
11
|
+
|
|
12
|
+
/// @internal
|
|
13
|
+
export type $Prose = MilkdownPlugin & {
|
|
14
|
+
plugin: () => Plugin
|
|
15
|
+
key: () => PluginKey | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Create a milkdown wrapper for [prosemirror plugin](https://prosemirror.net/docs/ref/#state.Plugin).
|
|
19
|
+
/// It takes a factory function which returns a [prosemirror plugin](https://prosemirror.net/docs/ref/#state.Plugin).
|
|
20
|
+
///
|
|
21
|
+
/// Additional property:
|
|
22
|
+
/// - `plugin`: The prosemirror plugin created.
|
|
23
|
+
/// - `key`: The [prosemirror plugin key](https://prosemirror.net/docs/ref/#state.PluginKey) of the plugin.
|
|
24
|
+
export function $prose(prose: (ctx: Ctx) => Plugin): $Prose {
|
|
25
|
+
let prosePlugin: Plugin | undefined
|
|
26
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
27
|
+
await ctx.wait(SchemaReady)
|
|
28
|
+
prosePlugin = prose(ctx)
|
|
29
|
+
ctx.update(prosePluginsCtx, (ps) => [...ps, prosePlugin!])
|
|
30
|
+
|
|
31
|
+
return () => {
|
|
32
|
+
ctx.update(prosePluginsCtx, (ps) => ps.filter((x) => x !== prosePlugin))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
;(<$Prose>plugin).plugin = () => prosePlugin!
|
|
36
|
+
;(<$Prose>plugin).key = () => prosePlugin!.spec.key
|
|
37
|
+
|
|
38
|
+
return <$Prose>plugin
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// The async version for `$prose`. You can use `await` in the factory when creating the plugin.
|
|
42
|
+
///
|
|
43
|
+
/// Additional property:
|
|
44
|
+
/// - `plugin`: The prosemirror plugin created.
|
|
45
|
+
/// - `key`: The [prosemirror plugin key](https://prosemirror.net/docs/ref/#state.PluginKey) of the plugin.
|
|
46
|
+
/// - `timer`: The timer which will be resolved when the plugin is ready.
|
|
47
|
+
export function $proseAsync(
|
|
48
|
+
prose: (ctx: Ctx) => Promise<Plugin>,
|
|
49
|
+
timerName?: string
|
|
50
|
+
) {
|
|
51
|
+
let prosePlugin: Plugin | undefined
|
|
52
|
+
const plugin = addTimer<$Prose>(
|
|
53
|
+
async (ctx) => {
|
|
54
|
+
await ctx.wait(SchemaReady)
|
|
55
|
+
prosePlugin = await prose(ctx)
|
|
56
|
+
ctx.update(prosePluginsCtx, (ps) => [...ps, prosePlugin!])
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
ctx.update(prosePluginsCtx, (ps) => ps.filter((x) => x !== prosePlugin))
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
editorStateTimerCtx,
|
|
63
|
+
timerName
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
plugin.plugin = () => prosePlugin!
|
|
67
|
+
plugin.key = () => prosePlugin!.spec.key
|
|
68
|
+
|
|
69
|
+
return plugin
|
|
70
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { Command } from '@jvs-milkdown/prose/state'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
KeymapReady,
|
|
6
|
+
editorStateTimerCtx,
|
|
7
|
+
keymapCtx,
|
|
8
|
+
type KeymapItem,
|
|
9
|
+
} from '@jvs-milkdown/core'
|
|
10
|
+
|
|
11
|
+
import { addTimer } from './utils'
|
|
12
|
+
|
|
13
|
+
/// @internal
|
|
14
|
+
export type Keymap = Record<string, Command | KeymapItem>
|
|
15
|
+
|
|
16
|
+
/// @internal
|
|
17
|
+
export type $Shortcut = MilkdownPlugin & {
|
|
18
|
+
keymap: Keymap
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Create a shortcut for the editor.
|
|
22
|
+
/// It takes a factory function which returns a [prosemirror keymap](https://prosemirror.net/docs/ref/#keymap).
|
|
23
|
+
///
|
|
24
|
+
/// Additional property:
|
|
25
|
+
/// - `keymap`: The prosemirror keymap created.
|
|
26
|
+
export function $shortcut(shortcut: (ctx: Ctx) => Keymap): $Shortcut {
|
|
27
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
28
|
+
await ctx.wait(KeymapReady)
|
|
29
|
+
const km = ctx.get(keymapCtx)
|
|
30
|
+
const keymap = shortcut(ctx)
|
|
31
|
+
const dispose = km.addObjectKeymap(keymap)
|
|
32
|
+
;(<$Shortcut>plugin).keymap = keymap
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
dispose()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return <$Shortcut>plugin
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// The async version for `$shortcut`. You can use `await` in the factory when creating the keymap.
|
|
43
|
+
///
|
|
44
|
+
/// Additional property:
|
|
45
|
+
/// - `keymap`: The prosemirror keymap created.
|
|
46
|
+
/// - `timer`: The timer which will be resolved when the plugin is ready.
|
|
47
|
+
export function $shortcutAsync(
|
|
48
|
+
shortcut: (ctx: Ctx) => Promise<Keymap>,
|
|
49
|
+
timerName?: string
|
|
50
|
+
) {
|
|
51
|
+
return addTimer<$Shortcut>(
|
|
52
|
+
async (ctx, plugin) => {
|
|
53
|
+
await ctx.wait(KeymapReady)
|
|
54
|
+
const km = ctx.get(keymapCtx)
|
|
55
|
+
const keymap = await shortcut(ctx)
|
|
56
|
+
const dispose = km.addObjectKeymap(keymap)
|
|
57
|
+
plugin.keymap = keymap
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
dispose()
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
editorStateTimerCtx,
|
|
64
|
+
timerName
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Ctx, MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type {
|
|
3
|
+
MarkViewConstructor,
|
|
4
|
+
NodeViewConstructor,
|
|
5
|
+
} from '@jvs-milkdown/prose/view'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
SchemaReady,
|
|
9
|
+
editorViewTimerCtx,
|
|
10
|
+
markViewCtx,
|
|
11
|
+
nodeViewCtx,
|
|
12
|
+
} from '@jvs-milkdown/core'
|
|
13
|
+
import { NodeType } from '@jvs-milkdown/prose/model'
|
|
14
|
+
|
|
15
|
+
import type { $Mark, $Node } from '.'
|
|
16
|
+
|
|
17
|
+
import { addTimer } from './utils'
|
|
18
|
+
|
|
19
|
+
/// @internal
|
|
20
|
+
export type $View<
|
|
21
|
+
T extends $Node | $Mark,
|
|
22
|
+
V extends NodeViewConstructor | MarkViewConstructor,
|
|
23
|
+
> = MilkdownPlugin & {
|
|
24
|
+
view: V
|
|
25
|
+
type: T
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// @internal
|
|
29
|
+
export type GetConstructor<T extends $Node | $Mark> = T extends $Node
|
|
30
|
+
? NodeViewConstructor
|
|
31
|
+
: T extends $Mark
|
|
32
|
+
? MarkViewConstructor
|
|
33
|
+
: NodeViewConstructor | MarkViewConstructor
|
|
34
|
+
|
|
35
|
+
/// Create a [prosemirror node/mark view](https://prosemirror.net/docs/ref/#view.NodeView) plugin.
|
|
36
|
+
/// It takes two arguments
|
|
37
|
+
/// - `type`: The node/mark plugin that needs to add a view.
|
|
38
|
+
/// - `view`: The factory that creates the view. It should return a function that returns a [node/mark view constructor](https://prosemirror.net/docs/ref/#view.NodeView).
|
|
39
|
+
///
|
|
40
|
+
/// Additional property:
|
|
41
|
+
/// - `view`: The view created.
|
|
42
|
+
/// - `type`: The node/mark plugin that needs to add a view.
|
|
43
|
+
export function $view<
|
|
44
|
+
T extends $Node | $Mark,
|
|
45
|
+
V extends NodeViewConstructor | MarkViewConstructor = GetConstructor<T>,
|
|
46
|
+
>(type: T, view: (ctx: Ctx) => V): $View<T, V> {
|
|
47
|
+
const plugin: MilkdownPlugin = (ctx) => async () => {
|
|
48
|
+
await ctx.wait(SchemaReady)
|
|
49
|
+
const v = view(ctx)
|
|
50
|
+
if (type.type(ctx) instanceof NodeType)
|
|
51
|
+
ctx.update(nodeViewCtx, (ps) => [
|
|
52
|
+
...ps,
|
|
53
|
+
[type.id, v] as [string, NodeViewConstructor],
|
|
54
|
+
])
|
|
55
|
+
else
|
|
56
|
+
ctx.update(markViewCtx, (ps) => [
|
|
57
|
+
...ps,
|
|
58
|
+
[type.id, v] as [string, MarkViewConstructor],
|
|
59
|
+
])
|
|
60
|
+
;(<$View<T, V>>plugin).view = v
|
|
61
|
+
;(<$View<T, V>>plugin).type = type
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
if (type.type(ctx) instanceof NodeType)
|
|
65
|
+
ctx.update(nodeViewCtx, (ps) => ps.filter((x) => x[0] !== type.id))
|
|
66
|
+
else ctx.update(markViewCtx, (ps) => ps.filter((x) => x[0] !== type.id))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return <$View<T, V>>plugin
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// The async version for `$view`. You can use `await` in the factory when creating the view.
|
|
74
|
+
///
|
|
75
|
+
/// Additional property:
|
|
76
|
+
/// - `view`: The view created.
|
|
77
|
+
/// - `type`: The node/mark plugin that needs to add a view.
|
|
78
|
+
/// - `timer`: The timer which will be resolved when the view is ready.
|
|
79
|
+
export function $viewAsync<
|
|
80
|
+
T extends $Node | $Mark,
|
|
81
|
+
V extends NodeViewConstructor | MarkViewConstructor = GetConstructor<T>,
|
|
82
|
+
>(type: T, view: (ctx: Ctx) => Promise<V>, timerName?: string) {
|
|
83
|
+
return addTimer<$View<T, V>>(
|
|
84
|
+
async (ctx, plugin) => {
|
|
85
|
+
await ctx.wait(SchemaReady)
|
|
86
|
+
const v = await view(ctx)
|
|
87
|
+
if (type.type(ctx) instanceof NodeType)
|
|
88
|
+
ctx.update(nodeViewCtx, (ps) => [
|
|
89
|
+
...ps,
|
|
90
|
+
[type.id, v] as [string, NodeViewConstructor],
|
|
91
|
+
])
|
|
92
|
+
else
|
|
93
|
+
ctx.update(markViewCtx, (ps) => [
|
|
94
|
+
...ps,
|
|
95
|
+
[type.id, v] as [string, MarkViewConstructor],
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
plugin.view = v
|
|
99
|
+
plugin.type = type
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
if (type.type(ctx) instanceof NodeType)
|
|
103
|
+
ctx.update(nodeViewCtx, (ps) => ps.filter((x) => x[0] !== type.id))
|
|
104
|
+
else ctx.update(markViewCtx, (ps) => ps.filter((x) => x[0] !== type.id))
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
editorViewTimerCtx,
|
|
108
|
+
timerName
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Mark, Node } from '@jvs-milkdown/prose/model'
|
|
2
|
+
|
|
3
|
+
import type { $Ctx } from '../$ctx'
|
|
4
|
+
|
|
5
|
+
import { $ctx } from '../$ctx'
|
|
6
|
+
|
|
7
|
+
/// @internal
|
|
8
|
+
export type $NodeAttr = $Ctx<
|
|
9
|
+
(node: Node) => Record<string, any>,
|
|
10
|
+
`${string}Attr`
|
|
11
|
+
>
|
|
12
|
+
|
|
13
|
+
/// Create a slice which contains the attributes for node schema.
|
|
14
|
+
export const $nodeAttr = (
|
|
15
|
+
name: string,
|
|
16
|
+
value: (node: Node) => Record<string, any> = () => ({})
|
|
17
|
+
): $NodeAttr => $ctx(value, `${name}Attr`)
|
|
18
|
+
|
|
19
|
+
/// @internal
|
|
20
|
+
export type $MarkAttr = $Ctx<
|
|
21
|
+
(node: Mark) => Record<string, any>,
|
|
22
|
+
`${string}Attr`
|
|
23
|
+
>
|
|
24
|
+
|
|
25
|
+
/// Create a slice which contains the attributes for mark schema.
|
|
26
|
+
export const $markAttr = (
|
|
27
|
+
name: string,
|
|
28
|
+
value: (mark: Mark) => Record<string, any> = () => ({})
|
|
29
|
+
): $MarkAttr => $ctx(value, `${name}Attr`)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Ctx } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { MarkSchema } from '@jvs-milkdown/transformer'
|
|
3
|
+
|
|
4
|
+
import type { $Ctx } from '../$ctx'
|
|
5
|
+
import type { $Mark } from '../$mark'
|
|
6
|
+
|
|
7
|
+
import { $ctx } from '../$ctx'
|
|
8
|
+
import { $mark } from '../$mark'
|
|
9
|
+
|
|
10
|
+
/// @internal
|
|
11
|
+
export type GetMarkSchema = (ctx: Ctx) => MarkSchema
|
|
12
|
+
|
|
13
|
+
/// @internal
|
|
14
|
+
export type $MarkSchema<T extends string> = [
|
|
15
|
+
schemaCtx: $Ctx<GetMarkSchema, T>,
|
|
16
|
+
schema: $Mark,
|
|
17
|
+
] & {
|
|
18
|
+
id: $Mark['id']
|
|
19
|
+
type: $Mark['type']
|
|
20
|
+
mark: $Mark
|
|
21
|
+
ctx: $Ctx<GetMarkSchema, T>
|
|
22
|
+
key: $Ctx<GetMarkSchema, T>['key']
|
|
23
|
+
extendSchema: (
|
|
24
|
+
handler: (prev: GetMarkSchema) => GetMarkSchema
|
|
25
|
+
) => $MarkSchema<T>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Create a plugin for mark schema.
|
|
29
|
+
/// The first parameter is the id of the mark schema.
|
|
30
|
+
/// The second parameter is the function that returns the mark schema.
|
|
31
|
+
///
|
|
32
|
+
/// The function will return a plugin with additional properties:
|
|
33
|
+
/// - `id`: The id of the mark schema.
|
|
34
|
+
/// - `type`: A function witch will return the type of the mark schema.
|
|
35
|
+
/// - `ctx`: The context of the mark schema.
|
|
36
|
+
/// - `mark`: The mark schema plugin.
|
|
37
|
+
/// - `schema`: The mark schema.
|
|
38
|
+
/// - `key`: The key of slice which contains the mark schema factory.
|
|
39
|
+
/// - `extendSchema`: A function witch will return a plugin that can extend the mark schema.
|
|
40
|
+
export function $markSchema<T extends string>(
|
|
41
|
+
id: T,
|
|
42
|
+
schema: GetMarkSchema
|
|
43
|
+
): $MarkSchema<T> {
|
|
44
|
+
const schemaCtx = $ctx(schema, id)
|
|
45
|
+
|
|
46
|
+
const markSchema = $mark(id, (ctx) => {
|
|
47
|
+
const userSchema = ctx.get(schemaCtx.key)
|
|
48
|
+
return userSchema(ctx)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const result = [schemaCtx, markSchema] as $MarkSchema<T>
|
|
52
|
+
result.id = markSchema.id
|
|
53
|
+
result.mark = markSchema
|
|
54
|
+
|
|
55
|
+
result.type = (ctx: Ctx) => markSchema.type(ctx)
|
|
56
|
+
result.ctx = schemaCtx
|
|
57
|
+
result.key = schemaCtx.key
|
|
58
|
+
result.extendSchema = (handler) => {
|
|
59
|
+
const nextSchema = handler(schema)
|
|
60
|
+
|
|
61
|
+
return $markSchema(id, nextSchema)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
}
|