@prosekit/extensions 0.0.1
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 +0 -0
- package/dist/list/style.css +84 -0
- package/dist/placeholder/style.css +8 -0
- package/dist/prosekit-extensions-blockquote.d.ts +11 -0
- package/dist/prosekit-extensions-blockquote.js +23 -0
- package/dist/prosekit-extensions-bold.d.ts +20 -0
- package/dist/prosekit-extensions-bold.js +56 -0
- package/dist/prosekit-extensions-code-block.js +21 -0
- package/dist/prosekit-extensions-code.d.ts +16 -0
- package/dist/prosekit-extensions-code.js +20 -0
- package/dist/prosekit-extensions-heading.d.ts +15 -0
- package/dist/prosekit-extensions-heading.js +53 -0
- package/dist/prosekit-extensions-horizontal-rule.js +23 -0
- package/dist/prosekit-extensions-italic.d.ts +20 -0
- package/dist/prosekit-extensions-italic.js +50 -0
- package/dist/prosekit-extensions-list.d.ts +14 -0
- package/dist/prosekit-extensions-list.js +41 -0
- package/dist/prosekit-extensions-placeholder.d.ts +23 -0
- package/dist/prosekit-extensions-placeholder.js +47 -0
- package/dist/prosekit-extensions-suggestion.d.ts +37 -0
- package/dist/prosekit-extensions-suggestion.js +159 -0
- package/dist/prosekit-extensions.d.ts +2 -0
- package/dist/prosekit-extensions.js +0 -0
- package/package.json +99 -0
- package/src/index.ts +1 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 ocavue
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
File without changes
|
@@ -0,0 +1,84 @@
|
|
1
|
+
/* ../../node_modules/.pnpm/prosemirror-flat-list@0.3.14/node_modules/prosemirror-flat-list/dist/style.css */
|
2
|
+
.prosemirror-flat-list {
|
3
|
+
padding: 0;
|
4
|
+
margin-top: 0;
|
5
|
+
margin-bottom: 0;
|
6
|
+
margin-left: 32px;
|
7
|
+
margin-bottom: 0;
|
8
|
+
position: relative;
|
9
|
+
display: list-item;
|
10
|
+
list-style: none;
|
11
|
+
}
|
12
|
+
.prosemirror-flat-list.ProseMirror-selectednode {
|
13
|
+
outline: none;
|
14
|
+
}
|
15
|
+
.prosemirror-flat-list.ProseMirror-selectednode:after {
|
16
|
+
content: "";
|
17
|
+
position: absolute;
|
18
|
+
left: -32px;
|
19
|
+
right: -2px;
|
20
|
+
top: -2px;
|
21
|
+
bottom: -2px;
|
22
|
+
border: 2px solid #8cf;
|
23
|
+
pointer-events: none;
|
24
|
+
}
|
25
|
+
.prosemirror-flat-list[data-list-kind=bullet] {
|
26
|
+
list-style: disc;
|
27
|
+
}
|
28
|
+
.prosemirror-flat-list[data-list-kind=ordered]::before {
|
29
|
+
position: absolute;
|
30
|
+
right: 100%;
|
31
|
+
font-variant-numeric: tabular-nums;
|
32
|
+
content: counter(prosemirror-flat-list-counter, decimal) ". ";
|
33
|
+
}
|
34
|
+
@supports (-moz-appearance: none) {
|
35
|
+
.prosemirror-flat-list[data-list-kind=ordered] {
|
36
|
+
contain: style;
|
37
|
+
counter-increment: prosemirror-flat-list-counter;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
@supports not (-moz-appearance: none) {
|
41
|
+
.prosemirror-flat-list[data-list-kind=ordered] {
|
42
|
+
counter-reset: prosemirror-flat-list-counter;
|
43
|
+
counter-increment: prosemirror-flat-list-counter;
|
44
|
+
}
|
45
|
+
.prosemirror-flat-list[data-list-kind=ordered] + .prosemirror-flat-list[data-list-kind=ordered] {
|
46
|
+
counter-reset: none;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
.prosemirror-flat-list[data-list-kind=task] > .list-marker {
|
50
|
+
position: absolute;
|
51
|
+
right: 100%;
|
52
|
+
text-align: center;
|
53
|
+
width: 1.5em;
|
54
|
+
width: 1lh;
|
55
|
+
}
|
56
|
+
.prosemirror-flat-list[data-list-kind=task] > .list-marker,
|
57
|
+
.prosemirror-flat-list[data-list-kind=task] > .list-marker * {
|
58
|
+
cursor: pointer;
|
59
|
+
}
|
60
|
+
.prosemirror-flat-list[data-list-kind=toggle] > .list-marker {
|
61
|
+
position: absolute;
|
62
|
+
right: 100%;
|
63
|
+
text-align: center;
|
64
|
+
width: 1.5em;
|
65
|
+
width: 1lh;
|
66
|
+
}
|
67
|
+
.prosemirror-flat-list[data-list-kind=toggle] > .list-marker::before {
|
68
|
+
content: "\23f7";
|
69
|
+
}
|
70
|
+
.prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable][data-list-collapsed] > .list-marker::before {
|
71
|
+
content: "\23f5";
|
72
|
+
}
|
73
|
+
.prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable] > .list-marker {
|
74
|
+
cursor: pointer;
|
75
|
+
}
|
76
|
+
.prosemirror-flat-list[data-list-kind=toggle]:not([data-list-collapsable]) > .list-marker {
|
77
|
+
opacity: 40%;
|
78
|
+
pointer-events: none;
|
79
|
+
}
|
80
|
+
.prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable][data-list-collapsed] > .list-content > *:nth-child(n + 2) {
|
81
|
+
display: none;
|
82
|
+
}
|
83
|
+
|
84
|
+
/* src/list/style.css */
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
declare function addBlockquoteSpec(): _prosekit_core.Extension<{
|
4
|
+
NODES: "blockquote";
|
5
|
+
}>;
|
6
|
+
/** @public */
|
7
|
+
declare function addBlockquote(): _prosekit_core.Extension<{
|
8
|
+
NODES: "blockquote";
|
9
|
+
}>;
|
10
|
+
|
11
|
+
export { addBlockquote, addBlockquoteSpec };
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// src/blockquote/index.ts
|
2
|
+
import { addNodeSpec, defineExtension } from "@prosekit/core";
|
3
|
+
function addBlockquoteSpec() {
|
4
|
+
return addNodeSpec({
|
5
|
+
name: "blockquote",
|
6
|
+
spec: {
|
7
|
+
content: "block+",
|
8
|
+
group: "block",
|
9
|
+
defining: true,
|
10
|
+
parseDOM: [{ tag: "blockquote" }],
|
11
|
+
toDOM() {
|
12
|
+
return ["blockquote", 0];
|
13
|
+
}
|
14
|
+
}
|
15
|
+
});
|
16
|
+
}
|
17
|
+
function addBlockquote() {
|
18
|
+
return defineExtension([addBlockquoteSpec()]);
|
19
|
+
}
|
20
|
+
export {
|
21
|
+
addBlockquote,
|
22
|
+
addBlockquoteSpec
|
23
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
declare function addBoldSpec(): _prosekit_core.Extension<{
|
4
|
+
MARKS: "bold";
|
5
|
+
}>;
|
6
|
+
declare function addBoldCommands(): _prosekit_core.Extension<{
|
7
|
+
COMMAND_ARGS: {
|
8
|
+
toggleBold: [];
|
9
|
+
};
|
10
|
+
}>;
|
11
|
+
declare function addBoldKeymap(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
12
|
+
/** @public */
|
13
|
+
declare function addBold(): _prosekit_core.Extension<{
|
14
|
+
MARKS: "bold";
|
15
|
+
COMMAND_ARGS: {
|
16
|
+
toggleBold: [];
|
17
|
+
};
|
18
|
+
}>;
|
19
|
+
|
20
|
+
export { addBold, addBoldCommands, addBoldKeymap, addBoldSpec };
|
@@ -0,0 +1,56 @@
|
|
1
|
+
// src/bold/index.ts
|
2
|
+
import {
|
3
|
+
addCommands,
|
4
|
+
addKeymap,
|
5
|
+
addMarkSpec,
|
6
|
+
defineExtension,
|
7
|
+
toggleMark
|
8
|
+
} from "@prosekit/core";
|
9
|
+
function addBoldSpec() {
|
10
|
+
return addMarkSpec({
|
11
|
+
name: "bold",
|
12
|
+
spec: {
|
13
|
+
parseDOM: [
|
14
|
+
{ tag: "strong" },
|
15
|
+
// This works around a Google Docs misbehavior where
|
16
|
+
// pasted content will be inexplicably wrapped in `<b>`
|
17
|
+
// tags with a font-weight normal.
|
18
|
+
{
|
19
|
+
tag: "b",
|
20
|
+
getAttrs: (node) => {
|
21
|
+
return typeof node !== "string" && node.style.fontWeight !== "normal" && null;
|
22
|
+
}
|
23
|
+
},
|
24
|
+
{ style: "font-weight=400", clearMark: (m) => m.type.name == "strong" },
|
25
|
+
{
|
26
|
+
style: "font-weight",
|
27
|
+
getAttrs: (value) => {
|
28
|
+
return typeof value === "string" && /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
],
|
32
|
+
toDOM() {
|
33
|
+
return ["em", 0];
|
34
|
+
}
|
35
|
+
}
|
36
|
+
});
|
37
|
+
}
|
38
|
+
function addBoldCommands() {
|
39
|
+
return addCommands({
|
40
|
+
toggleBold: () => toggleMark({ type: "bold" })
|
41
|
+
});
|
42
|
+
}
|
43
|
+
function addBoldKeymap() {
|
44
|
+
return addKeymap({
|
45
|
+
"Mod-b": toggleMark({ type: "bold" })
|
46
|
+
});
|
47
|
+
}
|
48
|
+
function addBold() {
|
49
|
+
return defineExtension([addBoldSpec(), addBoldCommands(), addBoldKeymap()]);
|
50
|
+
}
|
51
|
+
export {
|
52
|
+
addBold,
|
53
|
+
addBoldCommands,
|
54
|
+
addBoldKeymap,
|
55
|
+
addBoldSpec
|
56
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
// src/code-block/index.ts
|
2
|
+
import { addNodeSpec, defineExtension } from "@prosekit/core";
|
3
|
+
function addHorizontalRuleSpec() {
|
4
|
+
return addNodeSpec({
|
5
|
+
name: "horizontalRule",
|
6
|
+
spec: {
|
7
|
+
group: "block",
|
8
|
+
parseDOM: [{ tag: "hr" }],
|
9
|
+
toDOM() {
|
10
|
+
return ["hr"];
|
11
|
+
}
|
12
|
+
}
|
13
|
+
});
|
14
|
+
}
|
15
|
+
function addHorizontalRule() {
|
16
|
+
return defineExtension([addHorizontalRuleSpec()]);
|
17
|
+
}
|
18
|
+
export {
|
19
|
+
addHorizontalRule,
|
20
|
+
addHorizontalRuleSpec
|
21
|
+
};
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @public
|
5
|
+
*/
|
6
|
+
declare function addCodeSpec(): _prosekit_core.Extension<{
|
7
|
+
MARKS: "code";
|
8
|
+
}>;
|
9
|
+
/**
|
10
|
+
* @public
|
11
|
+
*/
|
12
|
+
declare function addCode(): _prosekit_core.Extension<{
|
13
|
+
MARKS: "code";
|
14
|
+
}>;
|
15
|
+
|
16
|
+
export { addCode, addCodeSpec };
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// src/code/index.ts
|
2
|
+
import { addMarkSpec, defineExtension } from "@prosekit/core";
|
3
|
+
function addCodeSpec() {
|
4
|
+
return addMarkSpec({
|
5
|
+
name: "code",
|
6
|
+
spec: {
|
7
|
+
parseDOM: [{ tag: "code" }],
|
8
|
+
toDOM() {
|
9
|
+
return ["code", 0];
|
10
|
+
}
|
11
|
+
}
|
12
|
+
});
|
13
|
+
}
|
14
|
+
function addCode() {
|
15
|
+
return defineExtension([addCodeSpec()]);
|
16
|
+
}
|
17
|
+
export {
|
18
|
+
addCode,
|
19
|
+
addCodeSpec
|
20
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
interface HeadingAttrs {
|
4
|
+
level: number;
|
5
|
+
}
|
6
|
+
declare function addHeadingSpec(): _prosekit_core.Extension<{
|
7
|
+
NODES: "heading";
|
8
|
+
}>;
|
9
|
+
declare function addHeadingInputRule(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
10
|
+
/** @public */
|
11
|
+
declare function addHeading(): _prosekit_core.Extension<{
|
12
|
+
NODES: "heading";
|
13
|
+
}>;
|
14
|
+
|
15
|
+
export { HeadingAttrs, addHeading, addHeadingInputRule, addHeadingSpec };
|
@@ -0,0 +1,53 @@
|
|
1
|
+
// src/heading/index.ts
|
2
|
+
import {
|
3
|
+
addInputRule,
|
4
|
+
addNodeSpec,
|
5
|
+
defineExtension,
|
6
|
+
getNodeType
|
7
|
+
} from "@prosekit/core";
|
8
|
+
import { textblockTypeInputRule } from "@prosekit/pm/inputrules";
|
9
|
+
function addHeadingSpec() {
|
10
|
+
return addNodeSpec({
|
11
|
+
name: "heading",
|
12
|
+
spec: {
|
13
|
+
attrs: { level: { default: 1 } },
|
14
|
+
content: "inline*",
|
15
|
+
group: "block",
|
16
|
+
defining: true,
|
17
|
+
parseDOM: [
|
18
|
+
{ tag: "h1", attrs: { level: 1 } },
|
19
|
+
{ tag: "h2", attrs: { level: 2 } },
|
20
|
+
{ tag: "h3", attrs: { level: 3 } },
|
21
|
+
{ tag: "h4", attrs: { level: 4 } },
|
22
|
+
{ tag: "h5", attrs: { level: 5 } },
|
23
|
+
{ tag: "h6", attrs: { level: 6 } }
|
24
|
+
],
|
25
|
+
toDOM(node) {
|
26
|
+
return [`h${node.attrs.level}`, 0];
|
27
|
+
}
|
28
|
+
}
|
29
|
+
});
|
30
|
+
}
|
31
|
+
function addHeadingInputRule() {
|
32
|
+
return addInputRule(({ schema }) => {
|
33
|
+
const nodeSpec = getNodeType(schema, "heading");
|
34
|
+
const inputRule = textblockTypeInputRule(
|
35
|
+
/^(#{1,6})\s/,
|
36
|
+
nodeSpec,
|
37
|
+
(match) => {
|
38
|
+
var _a, _b;
|
39
|
+
const level = (_b = (_a = match[1]) == null ? void 0 : _a.length) != null ? _b : 1;
|
40
|
+
return { level };
|
41
|
+
}
|
42
|
+
);
|
43
|
+
return [inputRule];
|
44
|
+
});
|
45
|
+
}
|
46
|
+
function addHeading() {
|
47
|
+
return defineExtension([addHeadingSpec(), addHeadingInputRule()]);
|
48
|
+
}
|
49
|
+
export {
|
50
|
+
addHeading,
|
51
|
+
addHeadingInputRule,
|
52
|
+
addHeadingSpec
|
53
|
+
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// src/horizontal-rule/index.ts
|
2
|
+
import { addNodeSpec, defineExtension } from "@prosekit/core";
|
3
|
+
function addBlockquoteSpec() {
|
4
|
+
return addNodeSpec({
|
5
|
+
name: "blockquote",
|
6
|
+
spec: {
|
7
|
+
content: "block+",
|
8
|
+
group: "block",
|
9
|
+
defining: true,
|
10
|
+
parseDOM: [{ tag: "blockquote" }],
|
11
|
+
toDOM() {
|
12
|
+
return ["blockquote", 0];
|
13
|
+
}
|
14
|
+
}
|
15
|
+
});
|
16
|
+
}
|
17
|
+
function addBlockquote() {
|
18
|
+
return defineExtension([addBlockquoteSpec()]);
|
19
|
+
}
|
20
|
+
export {
|
21
|
+
addBlockquote,
|
22
|
+
addBlockquoteSpec
|
23
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
declare function addItalicSpec(): _prosekit_core.Extension<{
|
4
|
+
MARKS: "italic";
|
5
|
+
}>;
|
6
|
+
declare function addItalicCommands(): _prosekit_core.Extension<{
|
7
|
+
COMMAND_ARGS: {
|
8
|
+
toggleItalic: [];
|
9
|
+
};
|
10
|
+
}>;
|
11
|
+
declare function addItalicKeymap(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
12
|
+
/** @public */
|
13
|
+
declare function addItalic(): _prosekit_core.Extension<{
|
14
|
+
MARKS: "italic";
|
15
|
+
COMMAND_ARGS: {
|
16
|
+
toggleItalic: [];
|
17
|
+
};
|
18
|
+
}>;
|
19
|
+
|
20
|
+
export { addItalic, addItalicCommands, addItalicKeymap, addItalicSpec };
|
@@ -0,0 +1,50 @@
|
|
1
|
+
// src/italic/index.ts
|
2
|
+
import {
|
3
|
+
addCommands,
|
4
|
+
addKeymap,
|
5
|
+
addMarkSpec,
|
6
|
+
defineExtension,
|
7
|
+
toggleMark
|
8
|
+
} from "@prosekit/core";
|
9
|
+
function addItalicSpec() {
|
10
|
+
return addMarkSpec({
|
11
|
+
name: "italic",
|
12
|
+
spec: {
|
13
|
+
parseDOM: [
|
14
|
+
{ tag: "i" },
|
15
|
+
{ tag: "em" },
|
16
|
+
{ style: "font-style=italic" },
|
17
|
+
{
|
18
|
+
style: "font-style=normal",
|
19
|
+
clearMark: (m) => m.type.name === "italic"
|
20
|
+
}
|
21
|
+
],
|
22
|
+
toDOM() {
|
23
|
+
return ["em", 0];
|
24
|
+
}
|
25
|
+
}
|
26
|
+
});
|
27
|
+
}
|
28
|
+
function addItalicCommands() {
|
29
|
+
return addCommands({
|
30
|
+
toggleItalic: () => toggleMark({ type: "italic" })
|
31
|
+
});
|
32
|
+
}
|
33
|
+
function addItalicKeymap() {
|
34
|
+
return addKeymap({
|
35
|
+
"Mod-i": toggleMark({ type: "italic" })
|
36
|
+
});
|
37
|
+
}
|
38
|
+
function addItalic() {
|
39
|
+
return defineExtension([
|
40
|
+
addItalicSpec(),
|
41
|
+
addItalicCommands(),
|
42
|
+
addItalicKeymap()
|
43
|
+
]);
|
44
|
+
}
|
45
|
+
export {
|
46
|
+
addItalic,
|
47
|
+
addItalicCommands,
|
48
|
+
addItalicKeymap,
|
49
|
+
addItalicSpec
|
50
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
declare function addListSpec(): _prosekit_core.Extension<{
|
4
|
+
NODES: "list";
|
5
|
+
}>;
|
6
|
+
declare function addListPlugins(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
7
|
+
declare function addListKeymap(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
8
|
+
declare function addListInputRules(): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
9
|
+
/** @public */
|
10
|
+
declare function addList(): _prosekit_core.Extension<{
|
11
|
+
NODES: "list";
|
12
|
+
}>;
|
13
|
+
|
14
|
+
export { addList, addListInputRules, addListKeymap, addListPlugins, addListSpec };
|
@@ -0,0 +1,41 @@
|
|
1
|
+
// src/list/index.ts
|
2
|
+
import {
|
3
|
+
addInputRule,
|
4
|
+
addKeymap,
|
5
|
+
addNodeSpec,
|
6
|
+
addPlugin,
|
7
|
+
defineExtension
|
8
|
+
} from "@prosekit/core";
|
9
|
+
import {
|
10
|
+
createListPlugins,
|
11
|
+
createListSpec,
|
12
|
+
listInputRules,
|
13
|
+
listKeymap
|
14
|
+
} from "prosemirror-flat-list";
|
15
|
+
function addListSpec() {
|
16
|
+
return addNodeSpec({ name: "list", spec: createListSpec() });
|
17
|
+
}
|
18
|
+
function addListPlugins() {
|
19
|
+
return addPlugin({ plugins: ({ schema }) => createListPlugins({ schema }) });
|
20
|
+
}
|
21
|
+
function addListKeymap() {
|
22
|
+
return addKeymap(listKeymap);
|
23
|
+
}
|
24
|
+
function addListInputRules() {
|
25
|
+
return addInputRule(() => listInputRules);
|
26
|
+
}
|
27
|
+
function addList() {
|
28
|
+
return defineExtension([
|
29
|
+
addListSpec(),
|
30
|
+
addListPlugins(),
|
31
|
+
addListKeymap(),
|
32
|
+
addListInputRules()
|
33
|
+
]);
|
34
|
+
}
|
35
|
+
export {
|
36
|
+
addList,
|
37
|
+
addListInputRules,
|
38
|
+
addListKeymap,
|
39
|
+
addListPlugins,
|
40
|
+
addListSpec
|
41
|
+
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
|
3
|
+
interface PlaceholderOptions {
|
4
|
+
/**
|
5
|
+
* The placeholder text to use.
|
6
|
+
*/
|
7
|
+
placeholder: string;
|
8
|
+
/**
|
9
|
+
* By default, the placeholder text will be shown whenever the current text
|
10
|
+
* cursor is in an empty node. If you only want to show the placeholder when
|
11
|
+
* the whole doc is empty, you can set this option to 'doc'.
|
12
|
+
*
|
13
|
+
* @default 'block'
|
14
|
+
*/
|
15
|
+
strategy?: 'doc' | 'block';
|
16
|
+
}
|
17
|
+
/**
|
18
|
+
* Add a placeholder text to the editor when the current block or document is
|
19
|
+
* empty.
|
20
|
+
*/
|
21
|
+
declare function addPlaceholder(options: PlaceholderOptions): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
22
|
+
|
23
|
+
export { PlaceholderOptions, addPlaceholder };
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// src/placeholder/index.ts
|
2
|
+
import { addPlugin } from "@prosekit/core";
|
3
|
+
import { Plugin } from "@prosekit/pm/state";
|
4
|
+
import { Decoration, DecorationSet } from "@prosekit/pm/view";
|
5
|
+
function addPlaceholder(options) {
|
6
|
+
return addPlugin({
|
7
|
+
plugins: [createPlaceholderPlugin(options)]
|
8
|
+
});
|
9
|
+
}
|
10
|
+
function createPlaceholderPlugin(options) {
|
11
|
+
return new Plugin({
|
12
|
+
props: {
|
13
|
+
decorations: (state) => {
|
14
|
+
if (options.strategy === "doc" && !isDocEmpty(state.doc)) {
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
const placeholderText = options.placeholder;
|
18
|
+
const deco = createPlaceholderDecoration(state, placeholderText);
|
19
|
+
if (!deco) {
|
20
|
+
return null;
|
21
|
+
}
|
22
|
+
return DecorationSet.create(state.doc, [deco]);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
});
|
26
|
+
}
|
27
|
+
function isDocEmpty(doc) {
|
28
|
+
var _a;
|
29
|
+
return doc.childCount <= 1 && !((_a = doc.firstChild) == null ? void 0 : _a.content.size);
|
30
|
+
}
|
31
|
+
function createPlaceholderDecoration(state, placeholderText) {
|
32
|
+
const { selection } = state;
|
33
|
+
if (!selection.empty)
|
34
|
+
return null;
|
35
|
+
const $pos = selection.$anchor;
|
36
|
+
const node = $pos.parent;
|
37
|
+
if (node.content.size > 0)
|
38
|
+
return null;
|
39
|
+
const before = $pos.before();
|
40
|
+
return Decoration.node(before, before + node.nodeSize, {
|
41
|
+
class: "prosekit-placeholder",
|
42
|
+
"data-placeholder": placeholderText
|
43
|
+
});
|
44
|
+
}
|
45
|
+
export {
|
46
|
+
addPlaceholder
|
47
|
+
};
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import * as _prosekit_core from '@prosekit/core';
|
2
|
+
import { EditorState } from '@prosekit/pm/state';
|
3
|
+
|
4
|
+
interface PredictionRule {
|
5
|
+
match: RegExp;
|
6
|
+
matchAfter?: RegExp;
|
7
|
+
}
|
8
|
+
/**
|
9
|
+
* @returns Return a Transaction object if you want to append a transaction to current state (using )
|
10
|
+
*/
|
11
|
+
type MatchHandler = (options: {
|
12
|
+
rule: PredictionRule;
|
13
|
+
match: RegExpMatchArray;
|
14
|
+
matchAfter: RegExpMatchArray | null;
|
15
|
+
state: EditorState;
|
16
|
+
dismiss: VoidFunction;
|
17
|
+
deleteMatch: VoidFunction;
|
18
|
+
}) => void;
|
19
|
+
interface SuggestionOptions {
|
20
|
+
rules: PredictionRule[];
|
21
|
+
onMatch: MatchHandler;
|
22
|
+
onDeactivate: VoidFunction;
|
23
|
+
/**
|
24
|
+
* You can pass this function if you want to skip the matching in some cases.
|
25
|
+
* By default, the plugin will only run the matching if the current selection
|
26
|
+
* is empty, and the selection is not inside a
|
27
|
+
* [code](https://prosemirror.net/docs/ref/#model.NodeSpec.code) node nor
|
28
|
+
* inside a mark with the name as `code`.
|
29
|
+
*/
|
30
|
+
isValid?: (options: {
|
31
|
+
state: EditorState;
|
32
|
+
}) => boolean;
|
33
|
+
}
|
34
|
+
|
35
|
+
declare function addSuggestion(options: SuggestionOptions): _prosekit_core.Extension<_prosekit_core.ExtensionTyping<string, string, _prosekit_core.CommandArgs>>;
|
36
|
+
|
37
|
+
export { PredictionRule, SuggestionOptions, addSuggestion };
|
@@ -0,0 +1,159 @@
|
|
1
|
+
// src/suggestion/index.ts
|
2
|
+
import { addPlugin } from "@prosekit/core";
|
3
|
+
|
4
|
+
// src/suggestion/plugin.ts
|
5
|
+
import { ProseKitError } from "@prosekit/core";
|
6
|
+
import { Plugin, PluginKey } from "@prosekit/pm/state";
|
7
|
+
import { Decoration, DecorationSet } from "@prosekit/pm/view";
|
8
|
+
|
9
|
+
// src/suggestion/is-valid.ts
|
10
|
+
function defaultIsValid({ state }) {
|
11
|
+
return state.selection.empty && !isInsideCode(state.selection.$from);
|
12
|
+
}
|
13
|
+
function isInsideCode($pos) {
|
14
|
+
for (let d = $pos.depth; d > 0; d--) {
|
15
|
+
if ($pos.node(d).type.spec.code) {
|
16
|
+
return true;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
return $pos.marks().some((mark) => mark.type.name === "code");
|
20
|
+
}
|
21
|
+
|
22
|
+
// src/suggestion/plugin.ts
|
23
|
+
var pluginKey = new PluginKey("prosemirror-prediction");
|
24
|
+
function getPluginState(state) {
|
25
|
+
return pluginKey.getState(state);
|
26
|
+
}
|
27
|
+
function getTrMeta(tr) {
|
28
|
+
return tr.getMeta(pluginKey);
|
29
|
+
}
|
30
|
+
function setTrMeta(tr, meta) {
|
31
|
+
return tr.setMeta(pluginKey, meta);
|
32
|
+
}
|
33
|
+
function createPredictionPlugin(options) {
|
34
|
+
if (options.rules.length === 0) {
|
35
|
+
throw new ProseKitError(
|
36
|
+
"You can't create a prediction plugin without rules"
|
37
|
+
);
|
38
|
+
}
|
39
|
+
const { onMatch, onDeactivate, isValid = defaultIsValid } = options;
|
40
|
+
return new Plugin({
|
41
|
+
key: pluginKey,
|
42
|
+
state: {
|
43
|
+
init: () => {
|
44
|
+
return { active: false, ignore: null, matching: null };
|
45
|
+
},
|
46
|
+
apply: (tr, prevValue, oldState, newState) => {
|
47
|
+
var _a;
|
48
|
+
const meta = getTrMeta(tr);
|
49
|
+
if (!tr.docChanged && oldState.selection.eq(newState.selection) && !meta) {
|
50
|
+
return prevValue;
|
51
|
+
}
|
52
|
+
if (meta) {
|
53
|
+
return meta;
|
54
|
+
}
|
55
|
+
if (!isValid({ state: newState })) {
|
56
|
+
return { active: false, ignore: null, matching: null };
|
57
|
+
}
|
58
|
+
const nextValue = calcPluginState(newState, options.rules);
|
59
|
+
if (nextValue.active && prevValue.ignore != null && ((_a = nextValue.matching) == null ? void 0 : _a.from) === prevValue.ignore) {
|
60
|
+
return prevValue;
|
61
|
+
}
|
62
|
+
return nextValue;
|
63
|
+
}
|
64
|
+
},
|
65
|
+
view: () => ({
|
66
|
+
update: (view, prevState) => {
|
67
|
+
const prevPluginState = getPluginState(prevState);
|
68
|
+
const currPluginState = getPluginState(view.state);
|
69
|
+
if ((currPluginState == null ? void 0 : currPluginState.active) && currPluginState.matching && currPluginState.matching.from !== currPluginState.ignore) {
|
70
|
+
const { from, to } = currPluginState.matching;
|
71
|
+
const dismiss = () => {
|
72
|
+
view.dispatch(
|
73
|
+
setTrMeta(view.state.tr, {
|
74
|
+
active: false,
|
75
|
+
ignore: from,
|
76
|
+
matching: null
|
77
|
+
})
|
78
|
+
);
|
79
|
+
};
|
80
|
+
const textContent = view.state.doc.textBetween(from, to, "\uFFFC");
|
81
|
+
const deleteMatch = () => {
|
82
|
+
if (view.state.doc.textBetween(from, to, "\uFFFC") === textContent) {
|
83
|
+
view.dispatch(view.state.tr.delete(from, to));
|
84
|
+
}
|
85
|
+
};
|
86
|
+
onMatch({
|
87
|
+
rule: currPluginState.matching.rule,
|
88
|
+
match: currPluginState.matching.match,
|
89
|
+
matchAfter: currPluginState.matching.matchAfter,
|
90
|
+
state: view.state,
|
91
|
+
dismiss,
|
92
|
+
deleteMatch
|
93
|
+
});
|
94
|
+
} else if (prevPluginState == null ? void 0 : prevPluginState.active) {
|
95
|
+
onDeactivate();
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}),
|
99
|
+
props: {
|
100
|
+
decorations: (state) => {
|
101
|
+
const pluginState = getPluginState(state);
|
102
|
+
if ((pluginState == null ? void 0 : pluginState.active) && pluginState.matching) {
|
103
|
+
const { from, to } = pluginState.matching;
|
104
|
+
const deco = Decoration.inline(from, to, {
|
105
|
+
class: "prosemirror-prediction-match"
|
106
|
+
});
|
107
|
+
return DecorationSet.create(state.doc, [deco]);
|
108
|
+
}
|
109
|
+
return null;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
});
|
113
|
+
}
|
114
|
+
function calcPluginState(state, rules) {
|
115
|
+
const { $anchor } = state.selection;
|
116
|
+
const matchAfter = rules.some((rule) => rule.matchAfter);
|
117
|
+
const parentOffset = $anchor.parentOffset;
|
118
|
+
const textBefore = $anchor.parent.textBetween(
|
119
|
+
Math.max(0, parentOffset - MAX_MATCH),
|
120
|
+
parentOffset,
|
121
|
+
null,
|
122
|
+
"\uFFFC"
|
123
|
+
);
|
124
|
+
const textAfter = matchAfter ? $anchor.parent.textBetween(
|
125
|
+
parentOffset,
|
126
|
+
Math.min(parentOffset + MAX_MATCH, $anchor.parent.content.size),
|
127
|
+
null
|
128
|
+
) : "";
|
129
|
+
for (const rule of rules) {
|
130
|
+
const match = textBefore.match(rule.match);
|
131
|
+
const matchAfter2 = rule.matchAfter ? textAfter.match(rule.matchAfter) : null;
|
132
|
+
if (match && match.index != null) {
|
133
|
+
const from = $anchor.pos - textBefore.length + match.index;
|
134
|
+
const to = $anchor.pos + (matchAfter2 ? matchAfter2[0].length : 0);
|
135
|
+
return {
|
136
|
+
active: true,
|
137
|
+
ignore: null,
|
138
|
+
matching: {
|
139
|
+
rule,
|
140
|
+
from,
|
141
|
+
to,
|
142
|
+
match,
|
143
|
+
matchAfter: matchAfter2
|
144
|
+
}
|
145
|
+
};
|
146
|
+
}
|
147
|
+
}
|
148
|
+
return { active: false };
|
149
|
+
}
|
150
|
+
var MAX_MATCH = 200;
|
151
|
+
|
152
|
+
// src/suggestion/index.ts
|
153
|
+
function addSuggestion(options) {
|
154
|
+
const plugin = createPredictionPlugin(options);
|
155
|
+
return addPlugin({ plugins: [plugin] });
|
156
|
+
}
|
157
|
+
export {
|
158
|
+
addSuggestion
|
159
|
+
};
|
File without changes
|
package/package.json
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
{
|
2
|
+
"name": "@prosekit/extensions",
|
3
|
+
"type": "module",
|
4
|
+
"version": "0.0.1",
|
5
|
+
"private": false,
|
6
|
+
"author": {
|
7
|
+
"name": "ocavue",
|
8
|
+
"email": "ocavue@gmail.com"
|
9
|
+
},
|
10
|
+
"license": "MIT",
|
11
|
+
"funding": "https://github.com/sponsors/ocavue",
|
12
|
+
"homepage": "https://github.com/ocavue/prosekit#readme",
|
13
|
+
"repository": {
|
14
|
+
"type": "git",
|
15
|
+
"url": "git+https://github.com/ocavue/prosekit.git",
|
16
|
+
"directory": "packages/extensions"
|
17
|
+
},
|
18
|
+
"bugs": {
|
19
|
+
"url": "https://github.com/ocavue/prosekit/issues"
|
20
|
+
},
|
21
|
+
"keywords": [
|
22
|
+
"ProseMirror"
|
23
|
+
],
|
24
|
+
"sideEffects": false,
|
25
|
+
"main": "./dist/prosekit-extensions.js",
|
26
|
+
"module": "./dist/prosekit-extensions.js",
|
27
|
+
"exports": {
|
28
|
+
".": {
|
29
|
+
"types": "./dist/prosekit-extensions.d.ts",
|
30
|
+
"import": "./dist/prosekit-extensions.js",
|
31
|
+
"default": "./dist/prosekit-extensions.js"
|
32
|
+
},
|
33
|
+
"./blockquote": {
|
34
|
+
"types": "./dist/prosekit-extensions-blockquote.d.ts",
|
35
|
+
"import": "./dist/prosekit-extensions-blockquote.js",
|
36
|
+
"default": "./dist/prosekit-extensions-blockquote.js"
|
37
|
+
},
|
38
|
+
"./bold": {
|
39
|
+
"types": "./dist/prosekit-extensions-bold.d.ts",
|
40
|
+
"import": "./dist/prosekit-extensions-bold.js",
|
41
|
+
"default": "./dist/prosekit-extensions-bold.js"
|
42
|
+
},
|
43
|
+
"./code": {
|
44
|
+
"types": "./dist/prosekit-extensions-code.d.ts",
|
45
|
+
"import": "./dist/prosekit-extensions-code.js",
|
46
|
+
"default": "./dist/prosekit-extensions-code.js"
|
47
|
+
},
|
48
|
+
"./heading": {
|
49
|
+
"types": "./dist/prosekit-extensions-heading.d.ts",
|
50
|
+
"import": "./dist/prosekit-extensions-heading.js",
|
51
|
+
"default": "./dist/prosekit-extensions-heading.js"
|
52
|
+
},
|
53
|
+
"./italic": {
|
54
|
+
"types": "./dist/prosekit-extensions-italic.d.ts",
|
55
|
+
"import": "./dist/prosekit-extensions-italic.js",
|
56
|
+
"default": "./dist/prosekit-extensions-italic.js"
|
57
|
+
},
|
58
|
+
"./list": {
|
59
|
+
"types": "./dist/prosekit-extensions-list.d.ts",
|
60
|
+
"import": "./dist/prosekit-extensions-list.js",
|
61
|
+
"default": "./dist/prosekit-extensions-list.js"
|
62
|
+
},
|
63
|
+
"./list/style.css": {
|
64
|
+
"default": "./dist/list/style.css"
|
65
|
+
},
|
66
|
+
"./placeholder": {
|
67
|
+
"types": "./dist/prosekit-extensions-placeholder.d.ts",
|
68
|
+
"import": "./dist/prosekit-extensions-placeholder.js",
|
69
|
+
"default": "./dist/prosekit-extensions-placeholder.js"
|
70
|
+
},
|
71
|
+
"./placeholder/style.css": {
|
72
|
+
"default": "./dist/placeholder/style.css"
|
73
|
+
},
|
74
|
+
"./suggestion": {
|
75
|
+
"types": "./dist/prosekit-extensions-suggestion.d.ts",
|
76
|
+
"import": "./dist/prosekit-extensions-suggestion.js",
|
77
|
+
"default": "./dist/prosekit-extensions-suggestion.js"
|
78
|
+
}
|
79
|
+
},
|
80
|
+
"files": [
|
81
|
+
"dist"
|
82
|
+
],
|
83
|
+
"dependencies": {
|
84
|
+
"@prosekit/core": "^0.0.1",
|
85
|
+
"@prosekit/pm": "^0.0.1",
|
86
|
+
"prosemirror-flat-list": "^0.3.14"
|
87
|
+
},
|
88
|
+
"devDependencies": {
|
89
|
+
"@prosekit/dev": "^0.0.0",
|
90
|
+
"tsup": "^7.1.0",
|
91
|
+
"typescript": "^5.1.6",
|
92
|
+
"vitest": "^0.33.0"
|
93
|
+
},
|
94
|
+
"scripts": {
|
95
|
+
"build:tsup": "tsup",
|
96
|
+
"build:tsc": "tsc -b tsconfig.json"
|
97
|
+
},
|
98
|
+
"types": "./dist/prosekit-extensions.d.ts"
|
99
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export {}
|