@kerebron/extension-basic-editor 0.4.27 → 0.4.29
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/esm/ExtensionBaseKeymap.d.ts +7 -0
- package/esm/ExtensionBaseKeymap.d.ts.map +1 -0
- package/esm/ExtensionBaseKeymap.js +56 -0
- package/esm/ExtensionBaseKeymap.js.map +1 -0
- package/esm/ExtensionBasicCodeEditor.d.ts +11 -0
- package/esm/ExtensionBasicCodeEditor.d.ts.map +1 -0
- package/esm/ExtensionBasicCodeEditor.js +62 -0
- package/esm/ExtensionBasicCodeEditor.js.map +1 -0
- package/esm/ExtensionBasicEditor.d.ts +49 -0
- package/esm/ExtensionBasicEditor.d.ts.map +1 -0
- package/esm/ExtensionBasicEditor.js +95 -0
- package/esm/ExtensionBasicEditor.js.map +1 -0
- package/esm/ExtensionDropcursor.d.ts +19 -0
- package/esm/ExtensionDropcursor.d.ts.map +1 -0
- package/esm/ExtensionDropcursor.js +187 -0
- package/esm/ExtensionDropcursor.js.map +1 -0
- package/esm/ExtensionGapcursor.d.ts +32 -0
- package/esm/ExtensionGapcursor.d.ts.map +1 -0
- package/esm/ExtensionGapcursor.js +250 -0
- package/esm/ExtensionGapcursor.js.map +1 -0
- package/esm/ExtensionHistory.d.ts +14 -0
- package/esm/ExtensionHistory.d.ts.map +1 -0
- package/esm/ExtensionHistory.js +35 -0
- package/esm/ExtensionHistory.js.map +1 -0
- package/esm/ExtensionHtml.d.ts +15 -0
- package/esm/ExtensionHtml.d.ts.map +1 -0
- package/esm/ExtensionHtml.js +100 -0
- package/esm/ExtensionHtml.js.map +1 -0
- package/esm/ExtensionMediaUpload.d.ts +24 -0
- package/esm/ExtensionMediaUpload.d.ts.map +1 -0
- package/esm/ExtensionMediaUpload.js +168 -0
- package/esm/ExtensionMediaUpload.js.map +1 -0
- package/esm/ExtensionSelection.d.ts +11 -0
- package/esm/ExtensionSelection.d.ts.map +1 -0
- package/esm/ExtensionSelection.js +230 -0
- package/esm/ExtensionSelection.js.map +1 -0
- package/esm/ExtensionTextAlign.d.ts +11 -0
- package/esm/ExtensionTextAlign.d.ts.map +1 -0
- package/esm/ExtensionTextAlign.js +40 -0
- package/esm/ExtensionTextAlign.js.map +1 -0
- package/esm/MarkBookmark.d.ts +8 -0
- package/esm/MarkBookmark.d.ts.map +1 -0
- package/esm/MarkBookmark.js +17 -0
- package/esm/MarkBookmark.js.map +1 -0
- package/esm/MarkChange.d.ts +8 -0
- package/esm/MarkChange.d.ts.map +1 -0
- package/esm/MarkChange.js +14 -0
- package/esm/MarkChange.js.map +1 -0
- package/esm/MarkCode.d.ts +11 -0
- package/esm/MarkCode.d.ts.map +1 -0
- package/esm/MarkCode.js +24 -0
- package/esm/MarkCode.js.map +1 -0
- package/esm/MarkHighlight.d.ts +8 -0
- package/esm/MarkHighlight.d.ts.map +1 -0
- package/esm/MarkHighlight.js +36 -0
- package/esm/MarkHighlight.js.map +1 -0
- package/esm/MarkItalic.d.ts +11 -0
- package/esm/MarkItalic.d.ts.map +1 -0
- package/esm/MarkItalic.js +30 -0
- package/esm/MarkItalic.js.map +1 -0
- package/esm/MarkLink.d.ts +8 -0
- package/esm/MarkLink.d.ts.map +1 -0
- package/esm/MarkLink.js +30 -0
- package/esm/MarkLink.js.map +1 -0
- package/esm/MarkStrike.d.ts +11 -0
- package/esm/MarkStrike.d.ts.map +1 -0
- package/esm/MarkStrike.js +27 -0
- package/esm/MarkStrike.js.map +1 -0
- package/esm/MarkStrong.d.ts +11 -0
- package/esm/MarkStrong.d.ts.map +1 -0
- package/esm/MarkStrong.js +39 -0
- package/esm/MarkStrong.js.map +1 -0
- package/esm/MarkSubscript.d.ts +11 -0
- package/esm/MarkSubscript.d.ts.map +1 -0
- package/esm/MarkSubscript.js +31 -0
- package/esm/MarkSubscript.js.map +1 -0
- package/esm/MarkSuperscript.d.ts +11 -0
- package/esm/MarkSuperscript.d.ts.map +1 -0
- package/esm/MarkSuperscript.js +31 -0
- package/esm/MarkSuperscript.js.map +1 -0
- package/esm/MarkTextColor.d.ts +8 -0
- package/esm/MarkTextColor.d.ts.map +1 -0
- package/esm/MarkTextColor.js +28 -0
- package/esm/MarkTextColor.js.map +1 -0
- package/esm/MarkUnderline.d.ts +11 -0
- package/esm/MarkUnderline.d.ts.map +1 -0
- package/esm/MarkUnderline.js +34 -0
- package/esm/MarkUnderline.js.map +1 -0
- package/esm/NodeAside.d.ts +8 -0
- package/esm/NodeAside.d.ts.map +1 -0
- package/esm/NodeAside.js +17 -0
- package/esm/NodeAside.js.map +1 -0
- package/esm/NodeBlockquote.d.ts +13 -0
- package/esm/NodeBlockquote.d.ts.map +1 -0
- package/esm/NodeBlockquote.js +35 -0
- package/esm/NodeBlockquote.js.map +1 -0
- package/esm/NodeBookmark.d.ts +8 -0
- package/esm/NodeBookmark.d.ts.map +1 -0
- package/esm/NodeBookmark.js +20 -0
- package/esm/NodeBookmark.js.map +1 -0
- package/esm/NodeBulletList.d.ts +13 -0
- package/esm/NodeBulletList.d.ts.map +1 -0
- package/esm/NodeBulletList.js +35 -0
- package/esm/NodeBulletList.js.map +1 -0
- package/esm/NodeCodeBlock.d.ts +9 -0
- package/esm/NodeCodeBlock.d.ts.map +1 -0
- package/esm/NodeCodeBlock.js +51 -0
- package/esm/NodeCodeBlock.js.map +1 -0
- package/esm/NodeDefinitionDesc.d.ts +8 -0
- package/esm/NodeDefinitionDesc.d.ts.map +1 -0
- package/esm/NodeDefinitionDesc.js +17 -0
- package/esm/NodeDefinitionDesc.js.map +1 -0
- package/esm/NodeDefinitionList.d.ts +13 -0
- package/esm/NodeDefinitionList.d.ts.map +1 -0
- package/esm/NodeDefinitionList.js +29 -0
- package/esm/NodeDefinitionList.js.map +1 -0
- package/esm/NodeDefinitionTerm.d.ts +8 -0
- package/esm/NodeDefinitionTerm.d.ts.map +1 -0
- package/esm/NodeDefinitionTerm.js +17 -0
- package/esm/NodeDefinitionTerm.js.map +1 -0
- package/esm/NodeDocument.d.ts +7 -0
- package/esm/NodeDocument.d.ts.map +1 -0
- package/esm/NodeDocument.js +20 -0
- package/esm/NodeDocument.js.map +1 -0
- package/esm/NodeDocumentCode.d.ts +7 -0
- package/esm/NodeDocumentCode.d.ts.map +1 -0
- package/esm/NodeDocumentCode.js +30 -0
- package/esm/NodeDocumentCode.js.map +1 -0
- package/esm/NodeFrontmatter.d.ts +8 -0
- package/esm/NodeFrontmatter.d.ts.map +1 -0
- package/esm/NodeFrontmatter.js +17 -0
- package/esm/NodeFrontmatter.js.map +1 -0
- package/esm/NodeHardBreak.d.ts +14 -0
- package/esm/NodeHardBreak.d.ts.map +1 -0
- package/esm/NodeHardBreak.js +68 -0
- package/esm/NodeHardBreak.js.map +1 -0
- package/esm/NodeHeading.d.ts +13 -0
- package/esm/NodeHeading.d.ts.map +1 -0
- package/esm/NodeHeading.js +50 -0
- package/esm/NodeHeading.js.map +1 -0
- package/esm/NodeHorizontalRule.d.ts +11 -0
- package/esm/NodeHorizontalRule.d.ts.map +1 -0
- package/esm/NodeHorizontalRule.js +30 -0
- package/esm/NodeHorizontalRule.js.map +1 -0
- package/esm/NodeImage.d.ts +8 -0
- package/esm/NodeImage.d.ts.map +1 -0
- package/esm/NodeImage.js +34 -0
- package/esm/NodeImage.js.map +1 -0
- package/esm/NodeInlineShortCode.d.ts +11 -0
- package/esm/NodeInlineShortCode.d.ts.map +1 -0
- package/esm/NodeInlineShortCode.js +40 -0
- package/esm/NodeInlineShortCode.js.map +1 -0
- package/esm/NodeListItem.d.ts +14 -0
- package/esm/NodeListItem.d.ts.map +1 -0
- package/esm/NodeListItem.js +202 -0
- package/esm/NodeListItem.js.map +1 -0
- package/esm/NodeMath.d.ts +8 -0
- package/esm/NodeMath.d.ts.map +1 -0
- package/esm/NodeMath.js +100 -0
- package/esm/NodeMath.js.map +1 -0
- package/esm/NodeOrderedList.d.ts +23 -0
- package/esm/NodeOrderedList.d.ts.map +1 -0
- package/esm/NodeOrderedList.js +53 -0
- package/esm/NodeOrderedList.js.map +1 -0
- package/esm/NodeParagraph.d.ts +11 -0
- package/esm/NodeParagraph.d.ts.map +1 -0
- package/esm/NodeParagraph.js +45 -0
- package/esm/NodeParagraph.js.map +1 -0
- package/esm/NodeTaskItem.d.ts +24 -0
- package/esm/NodeTaskItem.d.ts.map +1 -0
- package/esm/NodeTaskItem.js +147 -0
- package/esm/NodeTaskItem.js.map +1 -0
- package/esm/NodeTaskList.d.ts +11 -0
- package/esm/NodeTaskList.d.ts.map +1 -0
- package/esm/NodeTaskList.js +26 -0
- package/esm/NodeTaskList.js.map +1 -0
- package/esm/NodeText.d.ts +7 -0
- package/esm/NodeText.d.ts.map +1 -0
- package/esm/NodeText.js +10 -0
- package/esm/NodeText.js.map +1 -0
- package/esm/NodeVideo.d.ts +8 -0
- package/esm/NodeVideo.d.ts.map +1 -0
- package/esm/NodeVideo.js +46 -0
- package/esm/NodeVideo.js.map +1 -0
- package/esm/package.json +3 -0
- package/esm/remote-selection/ExtensionRemoteSelection.d.ts +24 -0
- package/esm/remote-selection/ExtensionRemoteSelection.d.ts.map +1 -0
- package/esm/remote-selection/ExtensionRemoteSelection.js +23 -0
- package/esm/remote-selection/ExtensionRemoteSelection.js.map +1 -0
- package/esm/remote-selection/remoteSelectionPlugin.d.ts +25 -0
- package/esm/remote-selection/remoteSelectionPlugin.d.ts.map +1 -0
- package/esm/remote-selection/remoteSelectionPlugin.js +97 -0
- package/esm/remote-selection/remoteSelectionPlugin.js.map +1 -0
- package/package.json +6 -3
- package/src/ExtensionBaseKeymap.ts +64 -0
- package/src/ExtensionBasicCodeEditor.ts +82 -0
- package/src/ExtensionBasicEditor.ts +97 -0
- package/src/ExtensionDropcursor.ts +221 -0
- package/src/ExtensionGapcursor.ts +278 -0
- package/src/ExtensionHistory.ts +48 -0
- package/src/ExtensionHtml.ts +158 -0
- package/src/ExtensionMediaUpload.ts +258 -0
- package/src/ExtensionSelection.ts +379 -0
- package/src/ExtensionTextAlign.ts +50 -0
- package/src/MarkBookmark.ts +20 -0
- package/src/MarkChange.ts +17 -0
- package/src/MarkCode.ts +35 -0
- package/src/MarkHighlight.ts +38 -0
- package/src/MarkItalic.ts +41 -0
- package/src/MarkLink.ts +32 -0
- package/src/MarkStrike.ts +38 -0
- package/src/MarkStrong.ts +52 -0
- package/src/MarkSubscript.ts +42 -0
- package/src/MarkSuperscript.ts +42 -0
- package/src/MarkTextColor.ts +29 -0
- package/src/MarkUnderline.ts +47 -0
- package/src/NodeAside.ts +19 -0
- package/src/NodeBlockquote.ts +51 -0
- package/src/NodeBookmark.ts +23 -0
- package/src/NodeBulletList.ts +51 -0
- package/src/NodeCodeBlock.ts +60 -0
- package/src/NodeDefinitionDesc.ts +19 -0
- package/src/NodeDefinitionList.ts +46 -0
- package/src/NodeDefinitionTerm.ts +19 -0
- package/src/NodeDocument.ts +22 -0
- package/src/NodeDocumentCode.ts +33 -0
- package/src/NodeFrontmatter.ts +19 -0
- package/src/NodeHardBreak.ts +92 -0
- package/src/NodeHeading.ts +76 -0
- package/src/NodeHorizontalRule.ts +43 -0
- package/src/NodeImage.ts +36 -0
- package/src/NodeInlineShortCode.ts +55 -0
- package/src/NodeListItem.ts +320 -0
- package/src/NodeMath.ts +109 -0
- package/src/NodeOrderedList.ts +79 -0
- package/src/NodeParagraph.ts +60 -0
- package/src/NodeTaskItem.ts +190 -0
- package/src/NodeTaskList.ts +38 -0
- package/src/NodeText.ts +12 -0
- package/src/NodeVideo.ts +44 -0
- package/src/remote-selection/ExtensionRemoteSelection.ts +45 -0
- package/src/remote-selection/remoteSelectionPlugin.ts +157 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Node } from 'prosemirror-model';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AnyExtensionOrReq,
|
|
5
|
+
Extension,
|
|
6
|
+
RawTextMapEntry,
|
|
7
|
+
RawTextResult,
|
|
8
|
+
} from '@kerebron/editor';
|
|
9
|
+
import { NodeDocumentCode } from './NodeDocumentCode.js';
|
|
10
|
+
|
|
11
|
+
import { ExtensionSelection } from './ExtensionSelection.js';
|
|
12
|
+
import { ExtensionBaseKeymap } from './ExtensionBaseKeymap.js';
|
|
13
|
+
import { ExtensionDropcursor } from './ExtensionDropcursor.js';
|
|
14
|
+
import { ExtensionGapcursor } from './ExtensionGapcursor.js';
|
|
15
|
+
import { ExtensionHtml } from './ExtensionHtml.js';
|
|
16
|
+
import { NodeText } from './NodeText.js';
|
|
17
|
+
import { ExtensionRemoteSelection } from './remote-selection/ExtensionRemoteSelection.js';
|
|
18
|
+
|
|
19
|
+
export class ExtensionBasicCodeEditor extends Extension {
|
|
20
|
+
name = 'basic-code-editor';
|
|
21
|
+
requires: AnyExtensionOrReq[];
|
|
22
|
+
|
|
23
|
+
constructor({ lang }: { lang: string }) {
|
|
24
|
+
super();
|
|
25
|
+
|
|
26
|
+
this.requires = [
|
|
27
|
+
new ExtensionBaseKeymap(),
|
|
28
|
+
new ExtensionDropcursor(),
|
|
29
|
+
new ExtensionGapcursor(),
|
|
30
|
+
new ExtensionHtml(),
|
|
31
|
+
new ExtensionRemoteSelection(),
|
|
32
|
+
new ExtensionSelection(),
|
|
33
|
+
new NodeDocumentCode({ lang }),
|
|
34
|
+
new NodeText(),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
toRawText(doc: Node): RawTextResult {
|
|
39
|
+
const topNodeType = this.editor.schema.topNodeType;
|
|
40
|
+
const spec = topNodeType.spec;
|
|
41
|
+
const singleNodeDoc = spec.content?.indexOf('*') === -1;
|
|
42
|
+
|
|
43
|
+
if (!singleNodeDoc) {
|
|
44
|
+
throw new Error('Not a single node doc');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (doc.children.length !== 1) {
|
|
48
|
+
throw new Error('Not a single node doc');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const codeBlock = doc.children[0];
|
|
52
|
+
|
|
53
|
+
const content = codeBlock.content.content
|
|
54
|
+
.map((node) => node.text)
|
|
55
|
+
.join('');
|
|
56
|
+
|
|
57
|
+
const lines = content.split('\n');
|
|
58
|
+
|
|
59
|
+
const rawTextMap: Array<RawTextMapEntry> = [];
|
|
60
|
+
|
|
61
|
+
let nodeIdx = 1;
|
|
62
|
+
let targetPos = 0;
|
|
63
|
+
let targetRow = 0;
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
rawTextMap.push({
|
|
66
|
+
nodeIdx: nodeIdx,
|
|
67
|
+
targetRow,
|
|
68
|
+
targetCol: 0,
|
|
69
|
+
targetPos,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
targetRow++;
|
|
73
|
+
targetPos += line.length + 1;
|
|
74
|
+
nodeIdx += line.length + 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
content,
|
|
79
|
+
rawTextMap,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Extension } from '@kerebron/editor';
|
|
2
|
+
import { ExtensionSelection } from './ExtensionSelection.js';
|
|
3
|
+
import { ExtensionBaseKeymap } from './ExtensionBaseKeymap.js';
|
|
4
|
+
import { ExtensionDropcursor } from './ExtensionDropcursor.js';
|
|
5
|
+
import { ExtensionGapcursor } from './ExtensionGapcursor.js';
|
|
6
|
+
import { ExtensionHtml } from './ExtensionHtml.js';
|
|
7
|
+
import { ExtensionMediaUpload } from './ExtensionMediaUpload.js';
|
|
8
|
+
import { ExtensionRemoteSelection } from './remote-selection/ExtensionRemoteSelection.js';
|
|
9
|
+
import { ExtensionTextAlign } from './ExtensionTextAlign.js';
|
|
10
|
+
|
|
11
|
+
import { MarkLink } from './MarkLink.js';
|
|
12
|
+
import { MarkStrong } from './MarkStrong.js';
|
|
13
|
+
import { MarkItalic } from './MarkItalic.js';
|
|
14
|
+
import { MarkUnderline } from './MarkUnderline.js';
|
|
15
|
+
import { MarkStrike } from './MarkStrike.js';
|
|
16
|
+
import { MarkCode } from './MarkCode.js';
|
|
17
|
+
import { MarkChange } from './MarkChange.js';
|
|
18
|
+
import { MarkBookmark } from './MarkBookmark.js';
|
|
19
|
+
import { MarkTextColor } from './MarkTextColor.js';
|
|
20
|
+
import { MarkHighlight } from './MarkHighlight.js';
|
|
21
|
+
import { MarkSuperscript } from './MarkSuperscript.js';
|
|
22
|
+
import { MarkSubscript } from './MarkSubscript.js';
|
|
23
|
+
|
|
24
|
+
import { NodeDocument } from './NodeDocument.js';
|
|
25
|
+
import { NodeText } from './NodeText.js';
|
|
26
|
+
import { NodeCodeBlock } from './NodeCodeBlock.js';
|
|
27
|
+
import { NodeBookmark } from './NodeBookmark.js';
|
|
28
|
+
import { NodeParagraph } from './NodeParagraph.js';
|
|
29
|
+
import { NodeHardBreak } from './NodeHardBreak.js';
|
|
30
|
+
import { NodeHorizontalRule } from './NodeHorizontalRule.js';
|
|
31
|
+
import { NodeOrderedList } from './NodeOrderedList.js';
|
|
32
|
+
import { NodeBulletList } from './NodeBulletList.js';
|
|
33
|
+
import { NodeListItem } from './NodeListItem.js';
|
|
34
|
+
import { NodeImage } from './NodeImage.js';
|
|
35
|
+
import { NodeVideo } from './NodeVideo.js';
|
|
36
|
+
import { NodeBlockquote } from './NodeBlockquote.js';
|
|
37
|
+
import { NodeAside } from './NodeAside.js';
|
|
38
|
+
import { NodeHeading } from './NodeHeading.js';
|
|
39
|
+
import { NodeMath } from './NodeMath.js';
|
|
40
|
+
import { NodeDefinitionList } from './NodeDefinitionList.js';
|
|
41
|
+
import { NodeDefinitionTerm } from './NodeDefinitionTerm.js';
|
|
42
|
+
import { NodeDefinitionDesc } from './NodeDefinitionDesc.js';
|
|
43
|
+
import { NodeFrontmatter } from './NodeFrontmatter.js';
|
|
44
|
+
import { NodeTaskList } from './NodeTaskList.js';
|
|
45
|
+
import { NodeTaskItem } from './NodeTaskItem.js';
|
|
46
|
+
import { NodeInlineShortCode } from './NodeInlineShortCode.js';
|
|
47
|
+
|
|
48
|
+
export class ExtensionBasicEditor extends Extension {
|
|
49
|
+
name = 'basic-editor';
|
|
50
|
+
requires = [
|
|
51
|
+
new ExtensionBaseKeymap(),
|
|
52
|
+
new ExtensionDropcursor(),
|
|
53
|
+
new ExtensionGapcursor(),
|
|
54
|
+
new ExtensionHtml(),
|
|
55
|
+
new ExtensionMediaUpload(),
|
|
56
|
+
new ExtensionRemoteSelection(),
|
|
57
|
+
new ExtensionSelection(),
|
|
58
|
+
new ExtensionTextAlign(),
|
|
59
|
+
new NodeDocument(),
|
|
60
|
+
new NodeText(),
|
|
61
|
+
new NodeParagraph(),
|
|
62
|
+
new NodeHardBreak(),
|
|
63
|
+
new NodeCodeBlock(),
|
|
64
|
+
new NodeBookmark(),
|
|
65
|
+
new NodeHorizontalRule(),
|
|
66
|
+
new NodeOrderedList(),
|
|
67
|
+
new NodeBulletList(),
|
|
68
|
+
new NodeListItem(),
|
|
69
|
+
new NodeTaskList(),
|
|
70
|
+
new NodeTaskItem(),
|
|
71
|
+
new NodeDefinitionList(),
|
|
72
|
+
new NodeDefinitionTerm(),
|
|
73
|
+
new NodeDefinitionDesc(),
|
|
74
|
+
new NodeTaskList(),
|
|
75
|
+
new NodeTaskItem(),
|
|
76
|
+
new NodeFrontmatter(),
|
|
77
|
+
new NodeImage(),
|
|
78
|
+
new NodeVideo(),
|
|
79
|
+
new NodeBlockquote(),
|
|
80
|
+
new NodeAside(),
|
|
81
|
+
new NodeHeading(),
|
|
82
|
+
new NodeMath(),
|
|
83
|
+
new NodeInlineShortCode(),
|
|
84
|
+
new MarkLink(),
|
|
85
|
+
new MarkItalic(),
|
|
86
|
+
new MarkStrong(),
|
|
87
|
+
new MarkUnderline(),
|
|
88
|
+
new MarkStrike(),
|
|
89
|
+
new MarkCode(),
|
|
90
|
+
new MarkChange(),
|
|
91
|
+
new MarkBookmark(),
|
|
92
|
+
new MarkTextColor(),
|
|
93
|
+
new MarkHighlight(),
|
|
94
|
+
new MarkSuperscript(),
|
|
95
|
+
new MarkSubscript(),
|
|
96
|
+
];
|
|
97
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { EditorState, Plugin } from 'prosemirror-state';
|
|
2
|
+
import { EditorView } from 'prosemirror-view';
|
|
3
|
+
import { dropPoint } from 'prosemirror-transform';
|
|
4
|
+
|
|
5
|
+
import { Extension } from '@kerebron/editor';
|
|
6
|
+
|
|
7
|
+
interface DropCursorOptions {
|
|
8
|
+
/// The color of the cursor. Defaults to `black`. Use `false` to apply no color and rely only on class.
|
|
9
|
+
color?: string | false;
|
|
10
|
+
|
|
11
|
+
/// The precise width of the cursor in pixels. Defaults to 1.
|
|
12
|
+
width?: number;
|
|
13
|
+
|
|
14
|
+
/// A CSS class name to add to the cursor element.
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Create a plugin that, when added to a ProseMirror instance,
|
|
19
|
+
/// causes a decoration to show up at the drop position when something
|
|
20
|
+
/// is dragged over the editor.
|
|
21
|
+
///
|
|
22
|
+
/// Nodes may add a `disableDropCursor` property to their spec to
|
|
23
|
+
/// control the showing of a drop cursor inside them. This may be a
|
|
24
|
+
/// boolean or a function, which will be called with a view and a
|
|
25
|
+
/// position, and should return a boolean.
|
|
26
|
+
export function dropCursor(options: DropCursorOptions = {}): Plugin {
|
|
27
|
+
return new Plugin({
|
|
28
|
+
view(editorView) {
|
|
29
|
+
return new DropCursorView(editorView, options);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DropCursorView {
|
|
35
|
+
width: number;
|
|
36
|
+
color: string | undefined;
|
|
37
|
+
class: string | undefined;
|
|
38
|
+
cursorPos: number | null = null;
|
|
39
|
+
element: HTMLElement | null = null;
|
|
40
|
+
timeout: number = -1;
|
|
41
|
+
handlers: { name: string; handler: (event: Event) => void }[];
|
|
42
|
+
|
|
43
|
+
constructor(readonly editorView: EditorView, options: DropCursorOptions) {
|
|
44
|
+
this.width = options.width ?? 1;
|
|
45
|
+
this.color = options.color === false
|
|
46
|
+
? undefined
|
|
47
|
+
: (options.color || 'black');
|
|
48
|
+
this.class = options.class;
|
|
49
|
+
|
|
50
|
+
this.handlers = ['dragover', 'dragend', 'drop', 'dragleave'].map((name) => {
|
|
51
|
+
let handler = (e: Event) => {
|
|
52
|
+
(this as any)[name](e);
|
|
53
|
+
};
|
|
54
|
+
editorView.dom.addEventListener(name, handler);
|
|
55
|
+
return { name, handler };
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
destroy() {
|
|
60
|
+
this.handlers.forEach(({ name, handler }) =>
|
|
61
|
+
this.editorView.dom.removeEventListener(name, handler)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
update(editorView: EditorView, prevState: EditorState) {
|
|
66
|
+
if (this.cursorPos != null && prevState.doc != editorView.state.doc) {
|
|
67
|
+
if (this.cursorPos > editorView.state.doc.content.size) {
|
|
68
|
+
this.setCursor(null);
|
|
69
|
+
} else this.updateOverlay();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setCursor(pos: number | null) {
|
|
74
|
+
if (pos == this.cursorPos) return;
|
|
75
|
+
this.cursorPos = pos;
|
|
76
|
+
if (pos == null) {
|
|
77
|
+
this.element!.parentNode!.removeChild(this.element!);
|
|
78
|
+
this.element = null;
|
|
79
|
+
} else {
|
|
80
|
+
this.updateOverlay();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
updateOverlay() {
|
|
85
|
+
let $pos = this.editorView.state.doc.resolve(this.cursorPos!);
|
|
86
|
+
let isBlock = !$pos.parent.inlineContent, rect;
|
|
87
|
+
let editorDOM = this.editorView.dom,
|
|
88
|
+
editorRect = editorDOM.getBoundingClientRect();
|
|
89
|
+
let scaleX = editorRect.width / editorDOM.offsetWidth,
|
|
90
|
+
scaleY = editorRect.height / editorDOM.offsetHeight;
|
|
91
|
+
if (isBlock) {
|
|
92
|
+
let before = $pos.nodeBefore, after = $pos.nodeAfter;
|
|
93
|
+
if (before || after) {
|
|
94
|
+
let node = this.editorView.nodeDOM(
|
|
95
|
+
this.cursorPos! - (before ? before.nodeSize : 0),
|
|
96
|
+
);
|
|
97
|
+
if (node) {
|
|
98
|
+
let nodeRect = (node as HTMLElement).getBoundingClientRect();
|
|
99
|
+
let top = before ? nodeRect.bottom : nodeRect.top;
|
|
100
|
+
if (before && after) {
|
|
101
|
+
top = (top +
|
|
102
|
+
(this.editorView.nodeDOM(this.cursorPos!) as HTMLElement)
|
|
103
|
+
.getBoundingClientRect().top) / 2;
|
|
104
|
+
}
|
|
105
|
+
let halfWidth = (this.width / 2) * scaleY;
|
|
106
|
+
rect = {
|
|
107
|
+
left: nodeRect.left,
|
|
108
|
+
right: nodeRect.right,
|
|
109
|
+
top: top - halfWidth,
|
|
110
|
+
bottom: top + halfWidth,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!rect) {
|
|
116
|
+
let coords = this.editorView.coordsAtPos(this.cursorPos!);
|
|
117
|
+
let halfWidth = (this.width / 2) * scaleX;
|
|
118
|
+
rect = {
|
|
119
|
+
left: coords.left - halfWidth,
|
|
120
|
+
right: coords.left + halfWidth,
|
|
121
|
+
top: coords.top,
|
|
122
|
+
bottom: coords.bottom,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let parent = this.editorView.dom.offsetParent as HTMLElement;
|
|
127
|
+
if (!this.element) {
|
|
128
|
+
this.element = parent.appendChild(document.createElement('div'));
|
|
129
|
+
if (this.class) this.element.className = this.class;
|
|
130
|
+
this.element.style.cssText =
|
|
131
|
+
'position: absolute; z-index: 50; pointer-events: none;';
|
|
132
|
+
if (this.color) {
|
|
133
|
+
this.element.style.backgroundColor = this.color;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
this.element.classList.toggle('prosemirror-dropcursor-block', isBlock);
|
|
137
|
+
this.element.classList.toggle('prosemirror-dropcursor-inline', !isBlock);
|
|
138
|
+
let parentLeft, parentTop;
|
|
139
|
+
if (
|
|
140
|
+
!parent ||
|
|
141
|
+
parent == document.body && getComputedStyle(parent).position == 'static'
|
|
142
|
+
) {
|
|
143
|
+
parentLeft = -pageXOffset;
|
|
144
|
+
parentTop = -pageYOffset;
|
|
145
|
+
} else {
|
|
146
|
+
let rect = parent.getBoundingClientRect();
|
|
147
|
+
let parentScaleX = rect.width / parent.offsetWidth,
|
|
148
|
+
parentScaleY = rect.height / parent.offsetHeight;
|
|
149
|
+
parentLeft = rect.left - parent.scrollLeft * parentScaleX;
|
|
150
|
+
parentTop = rect.top - parent.scrollTop * parentScaleY;
|
|
151
|
+
}
|
|
152
|
+
this.element.style.left = (rect.left - parentLeft) / scaleX + 'px';
|
|
153
|
+
this.element.style.top = (rect.top - parentTop) / scaleY + 'px';
|
|
154
|
+
this.element.style.width = (rect.right - rect.left) / scaleX + 'px';
|
|
155
|
+
this.element.style.height = (rect.bottom - rect.top) / scaleY + 'px';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
scheduleRemoval(timeout: number) {
|
|
159
|
+
clearTimeout(this.timeout);
|
|
160
|
+
this.timeout = setTimeout(() => this.setCursor(null), timeout);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
dragover(event: DragEvent) {
|
|
164
|
+
if (!this.editorView.editable) return;
|
|
165
|
+
let pos = this.editorView.posAtCoords({
|
|
166
|
+
left: event.clientX,
|
|
167
|
+
top: event.clientY,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
let node = pos && pos.inside >= 0 &&
|
|
171
|
+
this.editorView.state.doc.nodeAt(pos.inside);
|
|
172
|
+
let disableDropCursor = node && node.type.spec.disableDropCursor;
|
|
173
|
+
let disabled = typeof disableDropCursor == 'function'
|
|
174
|
+
? disableDropCursor(this.editorView, pos!, event)
|
|
175
|
+
: disableDropCursor;
|
|
176
|
+
|
|
177
|
+
if (pos && !disabled) {
|
|
178
|
+
let target = pos.pos;
|
|
179
|
+
if (this.editorView.dragging && this.editorView.dragging.slice) {
|
|
180
|
+
let point = dropPoint(
|
|
181
|
+
this.editorView.state.doc,
|
|
182
|
+
target,
|
|
183
|
+
this.editorView.dragging.slice,
|
|
184
|
+
);
|
|
185
|
+
if (point != null) target = point;
|
|
186
|
+
}
|
|
187
|
+
this.setCursor(target);
|
|
188
|
+
this.scheduleRemoval(5000);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
dragend() {
|
|
193
|
+
this.scheduleRemoval(20);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
drop() {
|
|
197
|
+
this.scheduleRemoval(20);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
dragleave(event: DragEvent) {
|
|
201
|
+
if (!this.editorView.dom.contains((event as any).relatedTarget)) {
|
|
202
|
+
this.setCursor(null);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export class ExtensionDropcursor extends Extension {
|
|
208
|
+
name = 'dropcursor';
|
|
209
|
+
|
|
210
|
+
options = {
|
|
211
|
+
color: 'currentColor',
|
|
212
|
+
width: 1,
|
|
213
|
+
class: undefined,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
override getProseMirrorPlugins(): Plugin[] {
|
|
217
|
+
return [
|
|
218
|
+
dropCursor(this.options),
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EditorState,
|
|
3
|
+
NodeSelection,
|
|
4
|
+
Plugin,
|
|
5
|
+
Selection,
|
|
6
|
+
TextSelection,
|
|
7
|
+
} from 'prosemirror-state';
|
|
8
|
+
import { Fragment, Node, ResolvedPos, Slice } from 'prosemirror-model';
|
|
9
|
+
import { Mappable } from 'prosemirror-transform';
|
|
10
|
+
import { Decoration, DecorationSet, EditorView } from 'prosemirror-view';
|
|
11
|
+
|
|
12
|
+
import type { Command } from '@kerebron/editor/commands';
|
|
13
|
+
import { Extension } from '@kerebron/editor';
|
|
14
|
+
import { keydownHandler } from '@kerebron/editor/plugins/keymap';
|
|
15
|
+
|
|
16
|
+
/// Gap cursor selections are represented using this class. Its
|
|
17
|
+
/// `$anchor` and `$head` properties both point at the cursor position.
|
|
18
|
+
export class GapCursor extends Selection {
|
|
19
|
+
/// Create a gap cursor.
|
|
20
|
+
constructor($pos: ResolvedPos) {
|
|
21
|
+
super($pos, $pos);
|
|
22
|
+
this.visible = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
map(doc: Node, mapping: Mappable): Selection {
|
|
26
|
+
let $pos = doc.resolve(mapping.map(this.head));
|
|
27
|
+
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override content() {
|
|
31
|
+
return Slice.empty;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
eq(other: Selection): boolean {
|
|
35
|
+
return other instanceof GapCursor && other.head == this.head;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
toJSON(): any {
|
|
39
|
+
return { type: 'gapcursor', pos: this.head };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// @internal
|
|
43
|
+
static fromJSONToGapCursor(doc: Node, json: any): GapCursor {
|
|
44
|
+
if (typeof json.pos != 'number') {
|
|
45
|
+
throw new RangeError('Invalid input for GapCursor.fromJSON');
|
|
46
|
+
}
|
|
47
|
+
return new GapCursor(doc.resolve(json.pos));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// @internal
|
|
51
|
+
override getBookmark(): GapBookmark {
|
|
52
|
+
return new GapBookmark(this.anchor);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// @internal
|
|
56
|
+
static valid($pos: ResolvedPos) {
|
|
57
|
+
let parent = $pos.parent;
|
|
58
|
+
if (parent.isTextblock || !closedBefore($pos) || !closedAfter($pos)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
let override = parent.type.spec.allowGapCursor;
|
|
62
|
+
if (override != null) return override;
|
|
63
|
+
let deflt = parent.contentMatchAt($pos.index()).defaultType;
|
|
64
|
+
return deflt && deflt.isTextblock;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// @internal
|
|
68
|
+
static findGapCursorFrom($pos: ResolvedPos, dir: number, mustMove = false) {
|
|
69
|
+
search: for (;;) {
|
|
70
|
+
if (!mustMove && GapCursor.valid($pos)) return $pos;
|
|
71
|
+
let pos = $pos.pos, next = null;
|
|
72
|
+
// Scan up from this position
|
|
73
|
+
for (let d = $pos.depth;; d--) {
|
|
74
|
+
let parent = $pos.node(d);
|
|
75
|
+
if (
|
|
76
|
+
dir > 0 ? $pos.indexAfter(d) < parent.childCount : $pos.index(d) > 0
|
|
77
|
+
) {
|
|
78
|
+
next = parent.child(dir > 0 ? $pos.indexAfter(d) : $pos.index(d) - 1);
|
|
79
|
+
break;
|
|
80
|
+
} else if (d == 0) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
pos += dir;
|
|
84
|
+
let $cur = $pos.doc.resolve(pos);
|
|
85
|
+
if (GapCursor.valid($cur)) return $cur;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// And then down into the next node
|
|
89
|
+
for (;;) {
|
|
90
|
+
let inside: Node | null = dir > 0 ? next.firstChild : next.lastChild;
|
|
91
|
+
if (!inside) {
|
|
92
|
+
if (
|
|
93
|
+
next.isAtom && !next.isText && !NodeSelection.isSelectable(next)
|
|
94
|
+
) {
|
|
95
|
+
$pos = $pos.doc.resolve(pos + next.nodeSize * dir);
|
|
96
|
+
mustMove = false;
|
|
97
|
+
continue search;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
next = inside;
|
|
102
|
+
pos += dir;
|
|
103
|
+
let $cur = $pos.doc.resolve(pos);
|
|
104
|
+
if (GapCursor.valid($cur)) return $cur;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Selection.jsonID('gapcursor', GapCursor);
|
|
113
|
+
|
|
114
|
+
class GapBookmark {
|
|
115
|
+
constructor(readonly pos: number) {}
|
|
116
|
+
|
|
117
|
+
map(mapping: Mappable) {
|
|
118
|
+
return new GapBookmark(mapping.map(this.pos));
|
|
119
|
+
}
|
|
120
|
+
resolve(doc: Node): Selection {
|
|
121
|
+
let $pos = doc.resolve(this.pos);
|
|
122
|
+
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function closedBefore($pos: ResolvedPos) {
|
|
127
|
+
for (let d = $pos.depth; d >= 0; d--) {
|
|
128
|
+
let index = $pos.index(d), parent = $pos.node(d);
|
|
129
|
+
// At the start of this parent, look at next one
|
|
130
|
+
if (index == 0) {
|
|
131
|
+
if (parent.type.spec.isolating) return true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// See if the node before (or its first ancestor) is closed
|
|
135
|
+
for (let before = parent.child(index - 1);; before = before.lastChild!) {
|
|
136
|
+
if (
|
|
137
|
+
(before.childCount == 0 && !before.inlineContent) || before.isAtom ||
|
|
138
|
+
before.type.spec.isolating
|
|
139
|
+
) return true;
|
|
140
|
+
if (before.inlineContent) return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Hit start of document
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function closedAfter($pos: ResolvedPos) {
|
|
148
|
+
for (let d = $pos.depth; d >= 0; d--) {
|
|
149
|
+
let index = $pos.indexAfter(d), parent = $pos.node(d);
|
|
150
|
+
if (index == parent.childCount) {
|
|
151
|
+
if (parent.type.spec.isolating) return true;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
for (let after = parent.child(index);; after = after.firstChild!) {
|
|
155
|
+
if (
|
|
156
|
+
(after.childCount == 0 && !after.inlineContent) || after.isAtom ||
|
|
157
|
+
after.type.spec.isolating
|
|
158
|
+
) return true;
|
|
159
|
+
if (after.inlineContent) return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Create a gap cursor plugin. When enabled, this will capture clicks
|
|
166
|
+
/// near and arrow-key-motion past places that don't have a normally
|
|
167
|
+
/// selectable position nearby, and create a gap cursor selection for
|
|
168
|
+
/// them. The cursor is drawn as an element with class
|
|
169
|
+
/// `kb-gapcursor`. You can either include
|
|
170
|
+
/// `style/gapcursor.css` from the package's directory or add your own
|
|
171
|
+
/// styles to make it visible.
|
|
172
|
+
function gapCursor(): Plugin {
|
|
173
|
+
return new Plugin({
|
|
174
|
+
props: {
|
|
175
|
+
decorations: drawGapCursor,
|
|
176
|
+
|
|
177
|
+
createSelectionBetween(_view, $anchor, $head) {
|
|
178
|
+
return $anchor.pos == $head.pos && GapCursor.valid($head)
|
|
179
|
+
? new GapCursor($head)
|
|
180
|
+
: null;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
handleClick,
|
|
184
|
+
handleKeyDown,
|
|
185
|
+
handleDOMEvents: { beforeinput: beforeinput as any },
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const handleKeyDown = keydownHandler({
|
|
191
|
+
'ArrowLeft': arrow('horiz', -1),
|
|
192
|
+
'ArrowRight': arrow('horiz', 1),
|
|
193
|
+
'ArrowUp': arrow('vert', -1),
|
|
194
|
+
'ArrowDown': arrow('vert', 1),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
function arrow(axis: 'vert' | 'horiz', dir: number): Command {
|
|
198
|
+
const dirStr = axis == 'vert'
|
|
199
|
+
? (dir > 0 ? 'down' : 'up')
|
|
200
|
+
: (dir > 0 ? 'right' : 'left');
|
|
201
|
+
return function (state, dispatch, view) {
|
|
202
|
+
let sel = state.selection;
|
|
203
|
+
let $start = dir > 0 ? sel.$to : sel.$from, mustMove = sel.empty;
|
|
204
|
+
if (sel instanceof TextSelection) {
|
|
205
|
+
if (!view!.endOfTextblock(dirStr) || $start.depth == 0) return false;
|
|
206
|
+
mustMove = false;
|
|
207
|
+
$start = state.doc.resolve(dir > 0 ? $start.after() : $start.before());
|
|
208
|
+
}
|
|
209
|
+
let $found = GapCursor.findGapCursorFrom($start, dir, mustMove);
|
|
210
|
+
if (!$found) return false;
|
|
211
|
+
if (dispatch) dispatch(state.tr.setSelection(new GapCursor($found)));
|
|
212
|
+
return true;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function handleClick(view: EditorView, pos: number, event: MouseEvent) {
|
|
217
|
+
if (!view || !view.editable) return false;
|
|
218
|
+
let $pos = view.state.doc.resolve(pos);
|
|
219
|
+
if (!GapCursor.valid($pos)) return false;
|
|
220
|
+
let clickPos = view.posAtCoords({ left: event.clientX, top: event.clientY });
|
|
221
|
+
if (
|
|
222
|
+
clickPos && clickPos.inside > -1 &&
|
|
223
|
+
NodeSelection.isSelectable(view.state.doc.nodeAt(clickPos.inside)!)
|
|
224
|
+
) return false;
|
|
225
|
+
view.dispatch(view.state.tr.setSelection(new GapCursor($pos)));
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
230
|
+
// is active, quickly creates an inline context for the composition to
|
|
231
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
232
|
+
// moved into a valid position.
|
|
233
|
+
function beforeinput(view: EditorView, event: InputEvent) {
|
|
234
|
+
if (
|
|
235
|
+
event.inputType != 'insertCompositionText' ||
|
|
236
|
+
!(view.state.selection instanceof GapCursor)
|
|
237
|
+
) return false;
|
|
238
|
+
|
|
239
|
+
let { $from } = view.state.selection;
|
|
240
|
+
let insert = $from.parent.contentMatchAt($from.index()).findWrapping(
|
|
241
|
+
view.state.schema.nodes.text,
|
|
242
|
+
);
|
|
243
|
+
if (!insert) return false;
|
|
244
|
+
|
|
245
|
+
let frag = Fragment.empty;
|
|
246
|
+
for (let i = insert.length - 1; i >= 0; i--) {
|
|
247
|
+
frag = Fragment.from(insert[i].createAndFill(null, frag));
|
|
248
|
+
}
|
|
249
|
+
let tr = view.state.tr.replace($from.pos, $from.pos, new Slice(frag, 0, 0));
|
|
250
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
251
|
+
view.dispatch(tr);
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function drawGapCursor(state: EditorState) {
|
|
256
|
+
if (!(state.selection instanceof GapCursor)) return null;
|
|
257
|
+
let node = document.createElement('div');
|
|
258
|
+
node.className = 'kb-gapcursor';
|
|
259
|
+
return DecorationSet.create(state.doc, [
|
|
260
|
+
Decoration.widget(state.selection.head, node, { key: 'gapcursor' }),
|
|
261
|
+
]);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export class ExtensionGapcursor extends Extension {
|
|
265
|
+
name = 'gapcursor';
|
|
266
|
+
|
|
267
|
+
options = {
|
|
268
|
+
color: 'currentColor',
|
|
269
|
+
width: 1,
|
|
270
|
+
class: undefined,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
override getProseMirrorPlugins(): Plugin[] {
|
|
274
|
+
return [
|
|
275
|
+
gapCursor(),
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
}
|